ref vs WeakMethod

It's hard to correctly accept a callback function and keep a weakref to it.

Problem

First, what works:

import inspect, weakref

class C:
   def m(self): pass

c = C()
w1 = weakref.ref(c, print)
del c
<weakref at 0x7f41575dcc20; dead>
# good

If the caller sends you a bound method, however, that code is wrong:

c = C()
w2 = weakref.ref(c.m, print)
<weakref at 0x7f4158da6250; dead>
# too soon- the bound method is transient and cleaned up
del c

There's a fix, though! IMHO it should be transparent, but here we are:

c = C()
w3 = weakref.WeakMethod(c.m, print)
del c
<weakref at 0x7f41577c1af0; dead>
# good

Of course, you want your API to accept not-bound-methods and bound-methods, just like anything else would. Can't rely on WeakMethod:

c = C()
w4 = weakref.WeakMethod(c, print)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.11/weakref.py", line 51, in __new__
    raise TypeError("argument should be a bound method, not {}"
TypeError: argument should be a bound method, not <class '__main__.C'>
# oh come on; there's only one thing I could want here

Solution

So the best I've figured out is to branch like this:

def print_when_cb_is_deleted1(cb):
  if inspect.ismethod(cb):
    w = weakref.WeakMethod(cb, print)
  else:
    w = weakref.ref(cb, print)
# ew

Or, barely less readable, factored out like this:

def print_when_cb_is_deleted2(cb):
  w = (weakref.WeakMethod if inspect.ismethod(cb) else weakref.ref)(cb, print)

Real code

FTR, here's my actual context. Deleting the callback shall unsubscribe the mqtt topic (if no other callbacks need it):

    async def subscribe(self, topic: str, cb: Callable[[float, str], None]):

        if topic not in self._subs:
            self._subs[topic] = []

        if inspect.ismethod(cb):
            ref = weakref.WeakMethod(cb, lambda _: self._cbDeleted(topic))
        else:
            ref = weakref.ref(cb, lambda _: self._cbDeleted(topic))

        self._subs[topic].append(ref)
        ...
            await self.mqttSub(topic)


    def _cbDeleted(self, topic: str):
        if topic not in self._subs:
            return
        self._subs[topic] = [v for v in self._subs[topic] if v() is not None]
        if not self._subs[topic]:
            asyncio.create_task(self.mqttUnsub(topic)) # small race condition here
            del self._subs[topic]

Building a local copy of pomerium into the ingest-controller

As you'd expect, the pomerium ingest-controller (IC) for k8s has some local code, some pomerium interface code, and depends on pomerium. If you want to try something with the pomerium library code, you need to arrange for a local version of it to end up in your IC container image.

Checkouts

% git clone https://git@github.com/pomerium/ingress-controller.git
ingress-controller% grep "pomerium v" go.mod 
    github.com/pomerium/pomerium v0.20.1-0.20230526203421-d315e683357a
ingress-controller% git clone https://git@github.com/pomerium/pomerium.git pomerium-src

I want pomerium source inside the IC directory for ease of docker building, but it needs a separate name from the ingress-controller/pomerium/ that's already there.

The grep call above says that this IC version depends on a pomerium-src revision (not just v0.20.1, which took me a while to figure out!)

ingress-controller/pomerium-src% git checkout d315e683357a

go.mod link

go.mod tells the IC build to use a version of pomerium from github. Insert this in go.mod (before the first require block, but maybe that doesn't matter):

replace github.com/pomerium/pomerium => ./pomerium-src

Incorporate pomerium-src's dependencies

Edit ingress-controller's Dockerfile, so the build can incorporate pomerium-src/go.mod.

diff --git a/Dockerfile b/Dockerfile
index 9805497..3044a97 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,10 +12,15 @@ COPY Makefile Makefile

 RUN mkdir -p pomerium/envoy/bin
 RUN make envoy
+
+COPY pomerium-src/go.mod pomerium-src/go.sum pomerium-src/
 RUN go mod download

 COPY Makefile ./Makefile

This docker (actually podman) build takes me 2m45s. I believe the following is a safe speedup. It does some dependency downloads before the source code copy, which caches better. New rebuild time is 2m0s:

diff --git a/Dockerfile b/Dockerfile
index 9805497..a9212d2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,6 +16,9 @@ RUN go mod download

 COPY Makefile ./Makefile

+# this pulls a bunch of deps that we don't want to repeat in 'make build-go'
+RUN make controller-gen
+
 # download ui dependencies from core module
 RUN mkdir -p internal
 RUN make internal/ui

Build the docker image with all the local edits

So far, this is building to ingress-controller/bin/*, but we want to affect the build in a docker image.

ingress-controller% export TAG=bang5:5000/pomerium_ic_local
ingress-controller% docker build -t ${TAG} . && docker push ${TAG}

Output includes this:

Successfully tagged bang5:5000/pomerium_ic_local:latest
031715bd3fa0da34f7cfdfc26eb020643bf1a9904ad8af228837c2bdcaef39cc

GCR error

You may see this:

       [3/3] STEP 1/5: FROM gcr.io/distroless/base:debug-nonroot@sha256:de8fb012fc630b7cdea6861442a0185213b574c71e246ddc97e9eb1d047048e7
    Trying to pull gcr.io/distroless/base@sha256:de8fb012fc630b7cdea6861442a0185213b574c71e246ddc97e9eb1d047048e7...
        Error: creating build container: initializing source docker://gcr.io/distroless/base@sha256:de8fb012fc630b7cdea6861442a0185213b574c71e246ddc97e9eb1d047048e7: getting username and password: 1 error occurred:
        * error getting credentials - err: docker-credential-gcloud resolves to executable in current directory (./docker-credential-gcloud), out: ``

Something from the user setup is breaking this (how?). I used a different account on another computer to work around it.

Use the local docker image in a k8s setup

I run ingress-controller/deployment.yaml with some kustomize patches, so I deploy the new docker image like this:

My existing deployment configs:

upstream% cp .../ingress-controller/deplyment.yaml pomerium-ingress-controller.yaml

My existing upstream/kustomzation.yaml:

bases:
  - pomerium-ingress-controller.yaml
  - cert-manager-v1.12.0.yaml
patchesStrategicMerge:
  - "patch.yaml"
etc

My upstream/patch.yaml, with the local image:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pomerium
  namespace: pomerium
spec:
  template:
    spec:
      containers:
        - name: pomerium
          image: bang5:5000/pomerium_ic_local:latest
          imagePullPolicy: Always

Apply changes (abridged):

kubectl kustomize upstream | kubectl apply -f -

Test the result

Edit this:

--- a/pomerium/ctrl/run.go
+++ b/pomerium/ctrl/run.go
@@ -75,6 +75,7 @@ func (r *Runner) Run(ctx context.Context) error {
        }

        log.FromContext(ctx).V(1).Info("got bootstrap config, starting pomerium...", "cfg", r.src.GetConfig())
+       log.Log.Error(nil, "hello IC world")

        return pomerium_cmd.Run(ctx, r.src)
 }

And this:

diff --git a/pomerium-src/authorize/internal/store/store.go b/pomerium-src/authorize/internal/store/store.go
index f9f9e08d..3dcac141 100644
--- a/pomerium-src/authorize/internal/store/store.go
+++ b/pomerium-src/authorize/internal/store/store.go
@@ -31,6 +31,8 @@ type Store struct {

 // New creates a new Store.
 func New() *Store {
+       ctx := context.TODO()
+       log.Error(ctx).Err(nil).Msg("hello pom world")
        return &Store{
                Store: inmem.New(),
        }

Rebuild

ingress-controller% docker build -t ${TAG} . && docker push ${TAG}

Also, my docker is podman, so I added "build --network=host" for a speedup.

Redeploy to k8s:

% kubectl rollout restart -n pomerium deploy/pomerium

Use tab-complete (or a app.name selector) to describe the right pod:

% kubectl describe -n pomerium pod/pomerium-79954677c-qzhpj

    Image:         bang5:5000/pomerium_ic_local:latest
    Image ID:      bang5:5000/pomerium_ic_local@sha256:0edbab12576bc68054b4f900f96318c04df4fcb9c1f588f2e4c2454cbe3b8a08

Not sure why this 0edb id doesn't match the 'successfully tagged' 0317 one above.

However, we still have great success!

% kubectl logs -n pomerium deploy/pomerium | grep hello

{"level":"error","ts":"2023-07-17T01:32:27Z","msg":"hello IC world"}
{"level":"error","time":"2023-07-17T01:32:27Z","message":"hello pom world"}

lf606 drone

http://mcumix.blogspot.com/2016/07/reverse-engineering-24ghz-rf-chip-xn297.html Recently, new chip appeared in many models - XN297. Although this chip seems to be a clone of nRF24L01+ - it has the same pinout, uses the same 16MHz crystal, the datasheet, partially translated to Chinese, looks very similar to the original, the register set is the same - it is not compatible by radio format.

https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf

DAC, maybe for barometer https://www.analog.com/media/en/technical-documentation/data-sheets/26071727fa.pdf

RGB led remote pixel FrontPage

(remains of a wiki site)

Pixel project

Features:

  • individually-addressable red/green/blue leds with 8-bit PWM brightness control
  • each pixel is under $5
  • pixels can be spaced apart and require only 2 wires chaining them together (or maybe 3)
  • possible compatibility with 1wire, allowing other devices to share the bus

Challenges:

  • Sending data with lots of power, possibly continuous power.
  • Reflecting LEDs to the right shape, and with complete mixing of the colors

Hardware sources:

Related projects:

Still looking for:

  • good comparison of LED brightnesses to common home and theater fixtures
  • a 1wire driver that sends lots of power (several amps)

countpara

countpara
Program to summarize groups of lines of text. Lines can be in paragraphs like this:
one
two

three
four
Or tagged with single-char keys like this:
a red
b green
b blue
a UV
Outputs look like this:
Table 1 **: 2 people (one, two)
Table 2 **: 2 people (three, four)
Table a **: 2 people (red, UV)
Table b **: 2 people (green, blue)
python source

Klein bottle topology demonstration

Click images for larger versions

AUTHOR

Drew Perttula

BRIEF DESCRIPTION

This project demonstrates a series of manipulations on Klein bottles and Mobius strips through the use of computer animation.

USAGE

The product animation may be useful as a teaching tool for students of topology or computer graphics (of course).

PLATFORM

I did my work on 3 machines running Linux and one Macintosh (for the video output only). While the programs, which are in C and Tcl, can be compiled and run on any platform that supports Geomview, the results of the project can be viewed with a VCR or web browser.

BRIEF DESCRIPTION

My final program has a Tk interface to a C++ program which sends geometry in real time to Geomview. By using the interface on the screen or sending it inter-application Tcl commands, one can obtain all the geometries visible in the movie. I wrote a script for StageTools, a set of Geomview modules, that sends instructions to my shape generation program and sends motion and output commands to Geomview.

The output is a video about 1:20s long (and these web pages). The left half of the page shows the images from the video spaced about two seconds apart (plus one extra, which shows the split bottle more clearly). Click any image to see a full-size version.

MAIN FUNCTION/APPROACH

Even though there are a few different shapes and movements shown in the video, my program has only one loop that moves all points one step in some direction, determined by 5 routines.

For the effect where the half-bottle bends into a simpler Mobius strip configuration, I used 3 spring functions. For every iteration, each control point (11x4 for the half-bottle, 25x4 for the ring sequences) is given a null vector called "force."

Then, in the operation I called "iso-grid," each point's force vector is pointed a fraction of the amount the point would have to move to equalize the lengths of the grid segments on all sides. I controlled the goal grid rectangle sizes in u and v in my interface.

Depending on the magnitude of the "v-straighten" parameter, each point's force vector is pointed a certain amount in the direction that would flatten the local cross-section in the v (small) axis. This parameter causes the half circle of the Klein bottle to become a straight line for the circular Mobius strip.

Finally, the "u-straighten" parameter performs a similar function as v-straighten, but in the perpendicular axis along the surface. However, the naive smoothing action of the v-straighten routine didn't look very good, nor did it cause the shape to approach a circle. So I rewrote u-straighten to find the angle between the neighboring points along the u-axis and push those two points closer into a line through the current point.

The last two routines causes points to travel towards some stage of the figure-8 bottle configuration. A parameter adjusts whether I get an open bottle (equivalent to a full-twist Mobius strip), or a complete Klein bottle.

To get the different actions in the video, my movie script (Tcl) adjusts the various parameters to turn on and off different sections of the point-adjustment loop. Here is a breakdown of the video, listing what routines are run in my program for each scene:

  • Load standard Klein bottle. My program actually never outputs more than half of the Klein bottle. I use Geomview to mirror the output to create the other half.

  • Load the figure-8 ("ring") immersion and attract all the points to the sealed-up position for a while. (This is how I show the ring model pre-built)

  • Load the standard immersion. (I use Geomview to show one half separating from the other)

  • For a sequence of frames, run the iso-grid, v-straighten, and u-straighten operations (described below) to make the half bottle bend into a simple Mobius strip.

  • Load the doubly-wrapped Mobius strip from my parameterization. For a sequence of frames, interpolate points on the strip to points in the unfolded ring. Only operate on points that are in the "strike zone," a factor that runs from 0 (no points) to 1 (affect all points) over the sequence. The strike zone check makes the effect appear to start at one point on the strip and travel around the strip.

  • For a sequence of frames, interpolate from the unfolded ring to the folded, sealed ring. The start and end points for each vertex are parameterized, and a similar strike zone effect is used.

STATISTICS

Using Geomview saved me from having to write any display functions at all!

Here are the line counts for the programs that I had to write. This does not count some other test code that I used. Don't be fooled by the small line counts! Tcl is a very compact language, and the C code merely has to evaluate my parameterizations and sum the various forces on each point. I used the same vector math library as Jordan Smith does on his projects.

Shape generator module for Geomview:
430 lines of C++
100 lines of Tcl

Movie script for StageTools:
150 lines of Tcl

Additional Geomview commands (lighting, etc):
50 lines (output from Geomview, after I set values with the UI)

FUTURE WORK

After discussion with Prof. Sequin, I would like to try some other transformations on Klein bottles. Hopefully, I will have a new movie soon that shows how to split a Klein bottle into one Mobius strip and bend that strip into the figure-8 immersion.

See my CS294 project

Auto gate latch

The images on this page are broken because the photo module for zope drifts around over the years. It's really hard to rescue old pics when they break, because they're locked up in ZODB. Use archive.org if you want to see them. -drewp 2007-02-27

20329

This is an electric gate latch. When you open the gate all the way, the gate presses the switch that turns on an electromagnet that holds the gate open. You can close the gate by pulling on it gently. The system is powered during the day only, so the gate cannot be left open at night. We can also close the gate from inside the house.

Here is the gate being held open:
7746

6902 This plate is mounted loosely so it can snap flat onto the electromagnet.
4030 This is a switch from a laser printer I took apart.
4627 This is an electromagnet from a laser printer I took apart. We don't know what it's rated for, but we give it about 12V. The wires are covered with something to make them weatherproof.

skim text file reader

by Drew Perttula and David McClosky

some highlighting on the #python irc channelusing skim to locate a particular discussion in one of my own IM chat logsskim helps me scan a source file for the part I'm interested in (a real-life skim usage!)another real-life use of skim to browse a source file

skim is an alternative text file viewer with a graphical interface and advanced searching and browsing features. It requires Python 2.2 or later with Tkinter, and it can make use of Wordnet with pywn.

The important features of skim are:

  • the birdseye view, where you can see a representation of your whole file (or a zoomed piece) at the same time that you browse the text

  • the advanced search features where you can, for example, highlight several words at once (like Google's cache) and even highlight related words (synonyms, misspellings)

  • the combination of the above two features; which means that you can see all the matches in your whole document at once. Now you can browse right to the clusters of matches. Compare this to your other programs, where you have to hit 'next' a bunch of times and wade through all the out-of-context matches. In skim, you can see instantly where the search matches occur together, which is probably the part you wanted to read.

Download

ez_install users can simply run easy_install skim.

You can download an archive as a gzipped tar file or zip file.

Skim uses the Python distutils. Run "python setup.py install" to install it.

GIS mapping with Python

Driving times from San Mateo County Forensic Lab to the rest of the bay area. Numbers are in minutes. Bright splotches are multiples of 10 minutes. Speed limits for each road class are from the TMRS project and might be experimental. There's a suspicious road classification on Willow Road at the east end of the Dumbarton Bridge that might be throwing off all the Fremont times. Code is in CVS at the tag 'sanmateo1'.

I presented the San Mateo image at dorkbot. Here is a photo of my presentation.


I made plots of driving times from my house, with every multiple of 5 minutes highlighted in yellow, and every minute alternating between light and dark. Red increases gradually.

Before I connected some gaps in the bridges, I was getting this wrong image. Here's the corrected one.


Here's a first version of running shortest-paths on the map. Streets are not yet weighted differently (and the coord system is still in degrees).


I also plotted the streets in the San Francisco Bay Area using these colors for the first letter of the street (except freeways):

1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Here is a similar google maps view. Mine looks stretched in width because Google's map uses some "reasonable" projection, whereas mine uses a "very simple" one.

This is based on my previous work with TMRS. Most of the new work was converting the drawing from pygame to a new python binding to the excellent agg library. The street data is from TIGER.

Code is in CVS at the tag 'color_first_letter'. viewcvs is here.

Related work

TimeContours

Travel-time Maps and their Uses

grepedit

Sometimes we grep for something in a bunch of files, and then we want to make edits to the lines that were returned from the grep. It's a hassle in most editors to open all the files and find all the lines. grepedit puts the match lines in a temp file for easy editing all at once.

The cmdline arguments to grepedit are the same as those to grep. Do not use arguments that alter the output line format; -C is ok.

grepedit also reads the following arguments:

--sort-text sort lines by the text part of the line (this probably doesn't make sense with -C)

Install

pip install grepedit

PyPI page

Download version 2.0 (2022-04-05) (py3 support- thanks Benjamin!)

Download version 1.0 (2005-10-09)

Screenshots

Run grepedit on itself and setup.py:

% grepedit the *

Edit the result lines in vi (according to $EDITOR):

grepedit:3:"""sometimes we grep for something in a bunch of files, and then we
grepedit:4:want to make edits to the lines that were returned from the grep. It's
grepedit:5:a hassle to edit all the files; this program puts the match lines in a
grepedit:8:EDITOR is used on the temporary file.
grepedit:10:The cmdline arguments to grepedit are the same as those to grep. Do
grepedit:11:not use arguments that alter the output line format; -C is ok.
grepedit:13:grepedit also reads the the following arguments:
grepedit:15:  --sort-text sort lines by the text part of the line (this probably
grepedit:100:                print "%s:%s has changed since the grep command ran- not modifying this line" % key
setup.py:6:      description="edit lines the result of a grep and modify the original files",
~
~
~
"/tmp/grepedit_IOLHsa" 10 lines, 792 characters

grepedit reports what changes it made:

% grepedit the *
no changes made in file setup.py
grepedit:13 substituting new line
% 

Related

emacs occur-edit-mode, new in emacs24 (a short howto)

hide-lines.el similar thing, but for working on one buffer in emacs

all.el --- Edit all lines matching a given regexp (in emacs)