Friday, 1 April 2022

Replacing Python's parser functionality?

First of all I want to mention that I know this is a horrible idea and it shouldn't be done. My intention is mainly curiosity and learning the innards of Python, and how to 'hack' them.

I was wondering whether it is at all possible to change what happens when we, for instance, use [] to create a list. Is there a way to modify how the parser behaves in order to, for instance, cause ["hello world"] to call print("hello world") instead of creating a list with one element?

I've attempted to find any documentation or posts about this but failed to do so.

Below is an example of replacing the built-in dict to instead use a custom class:

from __future__ import annotations
from typing import List, Any
import builtins


class Dict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

    def subset(self, keys: List[Any]) -> Dict:
        return Dict({key: self[key] for key in keys})


builtins.dict = Dict

When this module is imported, it replaces the dict built-in with the Dict class. However this only works when we directly call dict(). If we attempt to use {} it will fall back to the base dict built-in implementation:

import new_dict

a = dict({'a': 5, 'b': 8})
b = {'a': 5, 'b': 8}

print(type(a))
print(type(b))

Yields:

<class 'py_extensions.new_dict.Dict'>
<class 'dict'>


from Replacing Python's parser functionality?

Updating androidx-navigation from v2.3.5 to v2.4.0 causes map info window to disappear

A native android app I am working on has a ViewPager that slides between 2 fragments- one consisting of a list of locations and when a location is clicked, the ViewPager slides to the second fragment which consists of a map with a marker for that location and an info window with its name as seen in this image.

After updating androidx.navigation:navigation-fragment from v2.3.5 to v2.4.0, the info window occasionally fails to appears above the marker even though Marker.isInfoWindowShown() returns true shown here. This is code pertaining to setting the marker & info window in the map fragment:

private void setMarker(Place place) {
    if (map == null) {
        return;
    }

    map.clear();
    binding.mapContainer.requestFocus();
    selectedPlace = place;
    map.moveCamera(
        CameraUpdateFactory.newLatLngZoom(selectedPlace.getLatLng(), MAP_ZOOM_DEFAULT));

    MapMarkerTag markerTag = new MapMarkerTag();
    markerTag.setName(selectedPlace.getName());

    if (selectedPlace.getDrawable() == R.drawable.ic_parking_icon) {
        markerTag.setPermits(((ParkingLot) selectedPlace).getDisplayPermits());
    } else {
        markerTag.setPermits(getString(R.string.n_a));
    }

    MapInfoWindowAdapter infoWindowAdapter = new MapInfoWindowAdapter(requireActivity());
    map.setInfoWindowAdapter(infoWindowAdapter);
    Marker marker = map.addMarker(new MarkerOptions().position(selectedPlace.getLatLng()));
    Objects.requireNonNull(marker).setTag(markerTag);
    marker.showInfoWindow();
}

Here is the code for the custom info window adapter used in the app:

public class MapInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {

    private final Activity activity;

    public MapInfoWindowAdapter(Activity a) {
        this.activity = a;
    }

    @Override
    public View getInfoWindow(Marker marker) {
        View view = activity.getLayoutInflater().inflate(R.layout.map_info_window, null);

        TextView name = view.findViewById(R.id.info_window_name);
        TextView permitText = view.findViewById(R.id.info_window_permits_label);
        TextView permits = view.findViewById(R.id.info_window_permits);

        MapMarkerTag markerTag = (MapMarkerTag) marker.getTag();

        name.setText(Objects.requireNonNull(markerTag).getName());
        String permit = markerTag.getPermits();
        if (permit.equals(activity.getString(R.string.n_a))) {
            permitText.setVisibility(View.GONE);
            permits.setVisibility(View.GONE);
        } else {
            permits.setText(markerTag.getPermits());
        }

        return view;
    }

    @Override
    public View getInfoContents(@NonNull Marker marker) {
        return null;
    }
}

Furthermore, when clicking the bottom navigation view to navigate away from the map fragment, the MapInfoWindowAdapter.getInfoWindow is called with markerTag being null resulting in a NullPointerException and the app crashing.

Does anybody know why and how updating the androidx.navigation:navigation-fragment dependency would cause such a bug?

Thank you in advance.



from Updating androidx-navigation from v2.3.5 to v2.4.0 causes map info window to disappear

How to trim (crop) bottom whitespace of a PDF document, in memory

I am using wkhtmltopdf to render a (Django-templated) HTML document to a single-page PDF file. I would like to either render it immediately with the correct height (which I've failed to do so far) or render it incorrectly and trim it. I'm using Python.

Attempt type 1

  • wkhtmltopdf render to a very, very long single-page PDF with a lot of extra space using --page-height
  • Use pdfCropMargins to trim: crop(["-p4", "100", "0", "100", "100", "-a4", "0", "-28", "0", "0", "input.pdf"])

The PDF is rendered perfectly with 28 units of margin at the bottom, but I had to use the filesystem to execute the crop command. It seems that the tool expects an input file and output file, and also creates temporary files midway through. So I can't use it.

Attempt type 2

  • wkhtmltopdf render to multi-page PDF with default parameters
  • Use PyPDF4 (or PyPDF2) to read the file and combine pages into a long, single page

The PDF is rendered fine-ish in most cases, however, sometimes a lot of extra white space can be seen on the bottom if by chance the last PDF page had very little content.

Ideal scenario

The ideal scenario would involve a function that takes HTML and renders it into a single-page PDF with the expected amount of white space at the bottom. I would be happy with rendering the PDF using wkhtmltopdf, since it returns bytes, and later processing these bytes to remove any extra white space. But I don't want to involve the file system in this, as instead, I want to perform all operations in memory. Perhaps I can somehow inspect the PDF directly and remove the white space manually, or do some HTML magic to determine the render height before-hand?



from How to trim (crop) bottom whitespace of a PDF document, in memory