Skip to content

Globals and devices should be in modules, not builtins: shot_globals and shot_devices #11

Open
@philipstarkey

Description

@philipstarkey

Original report (archived issue) by Chris Billington (Bitbucket: cbillington, GitHub: chrisjbillington).


I used to not think it mattered much that we were modifying builtins to put our globals in them. Mainly because, although completely sacrilegious, we have a sort-of domain specific language here, damnit, we should be able to do what we like.

But more pragmatically I figured it wouldn't affect anything. Our code is the toplevel code, and nothing is relying on it conforming to good practices. Code we use will be unaffected because scoping rules.

This is not true. There is correct, unbuggy code out there that relies on getting NameErrors on things:

#!bash

bilbo:~ $ grep -R 'except NameError' /usr/lib/python2.7/ | wc -l
301

often for telling the difference between different versions of Python, but also for other totally innocent reasons.

But also I acknowledge that deviating from accepted practices confuses people, even when it doesn't to do any harm. This inhibits their ability to make good assumptions about how your software behaves.

So all the code that puts stuff into builtins should instead put them in two programmatically created modules, called shot_globals and shot_devices. These will be inserted into sys.modules so the user can import them from anywhere.

The user will probably want to import * from both (but they don't have to), and either way it makes it clearer in there code where stuff can be coming from, and no code that doesn't want to see our crap has to.

The only other question is: do we still make device instantiation put device objects into the global namespace they're being defined in? This is important because the user will often be immediately using the device they just defined as the parent of subsequently defined child devices.

I think we should not do this. The user should define their connection table and then do from shot_devices import * (or whatever they want to import).

To handle the fact that devices are needed within the connection table, we should change it such that parent devices are specified with strings instead of python identidiers from the current namespace.

Any other methods the user wants to call on devices halfway through the connection table should generally wait until the end, but if necessary, the user can import shot_devices as is, or import specific devices from it at any point. So long as they save their import * until the end. I can add an import hook that will take note of import * for the shot_devices module, such that Device.__init__ can raise an exception if a new device is being added after an import * was done. Using the same introspection that I'll be using to give labscript users better errors about compilation, I'll even be able to store the traceback to the import * call. No more of this `error: something was imported once, I don't know when, but don't do that!' like h5_lock and lyse's figure manager do (both of which should be changed to turn on their functionality on a function call, rather than as an import side effect).

Discussion welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions