-
Notifications
You must be signed in to change notification settings - Fork 56
Relocate labscript_devices import machinery #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Relocate labscript_devices import machinery #61
Conversation
The labscript_devices import machinery has now been relocated to labscript_utils as the first step in breaking the circular dependency between labscript-devices and blacs packages. The code in _import_machinery.py is equivalent to that in labscript_devices/__init__.py exept that `__name__` has been replaced with `'labscript_devices'`. There is also some code that chooses whether to use the old labscript_devices code or the new labscript_utils code depending on the version of labscript_devices. The specific version will need to be adjusted to match whatever version removes the code from labscript_devices (and even then, would not catch the case of users with local changes in a development install) so this may not be the best approach, but I couldn't think of another way to ensure compatibility between old and new labscript-suite components without reintroducing a different circular dependency. The labscript_devices `__init__.py` file can then be replaced with `from labscript_utils.labscript_devices import *` so as not to need to update every device class file.
Sweet! This seems like a good way to break the circular dependency. labscript_devices will now just be device code. I'll just try to wrap my head around this conditional import in |
Co-authored-by: Russell Anderson <5637107+rpanderson@users.noreply.github.com>
Co-authored-by: Russell Anderson <5637107+rpanderson@users.noreply.github.com>
I can explain the conditional import if you like in detail of you like but the main idea is that there are 8 permutations of old/new labscript-utils, labscript-devices and BLACS (or runviewer) and this ensures they all work without attempting to build the device registry more than once. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet, I get it.
Two things:
labscript-devices
may not be installed (after all it is not a dependency oflabscript-utils
!), so getting its version may fail. Either this permutation also needs to be taken into account, or (and I think this is more idiomatic) you could use the ability to import the machinery fromlabscript_devices
as a more direct test of whether it's there, rather than basing it on the version, such that__init__.py
would be something like:
try:
from labscript_devices import ClassRegister
except ImportError:
# labscript-devices >= 3.1, device registry now lives here:
from ._import_machinery import *
else:
# labscript-devices < 3.1, use device registry from there:
from labscript_devices import *
- Another (sub)module named
labscript_devices
puts a bad taste in my mouth. How aboutdevice_registry
or similar? There are no devices here, just a registry of them and the functions for registering them. Wouldn't mind naming_import_machinery
the same as whatever you call the directory but with a leading underscore, i.e._device_registry
, but whatever.
import importlib | ||
import packaging.version | ||
|
||
labscript_device_version_str = importlib.metadata.version('labscript_devices') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
labscript_devices may not be installed. Should catch whatever exception importlib.metadata raises in this case and presumably use _import_machinery in this case.
This doesn't work. In 3.1, (with
Yeah, that's a good point. I was following the convention I made for labscript-c-extensions, but in this case the code is not just for labscript_devices, but for BLACS and runviewer as well. |
So I don't think the proposed try/except method propose will work because even though an import error occurs (because So I've wrapped the call to importlib_metadata in a try/except instead which seems to do the trick. Again, the specific version in this code is dependent on changes to labscript_devices being merged and published as 3.1.0.dev16 (which should happen if it is the next PR to be merged). I'll make that PR now. Renamed the files/folders here as requested. |
Actually I'm a little bit confused about versions. Locally my labscript_devices (tip of master) says 3.0.0b3.dev3 but test PyPI says 3.1.0.dev16? Shouldn't these be the same? |
Distributed versions use the Now that In the meantime, you can get a local version consistent with that on Test PyPI by setting the above environment variable as described (provided you have setuptools_scm>=4.1.0). Update: See labscript-suite/labscript-suite#53. |
That's also a little odd, as even with Actually, you are likely reporting the version Edited to redact advice about running |
Running EDIT: Actually we would never expect the editable installs to report major version numbers because these will be in the maintenance branch...this could be a problem for upgrading editable installs. |
It depends. We don't use importlib.metadata for developer installations to get the |
Sorry! This wasn't the case a few days ago (before we bumped versions in setup.cfg). My advice should have been to source the version from the |
If I do that then new labscript_devices cannot import the import machinery from labscript utils due to the circular import (that for some reason does not populate labscript_devices with the contents of labscript_utils.device_registry, presumably because that import has not complete yet) |
So importlib_metadata doesn't work for this PR (due to editable install issue above) and neither does circular imports out of the box. I guess I could do the circular import and then monkey patch in the import machinery into labscript_devices from labscript_utils? That's a bit hacky though... @chrisjbillington do you have any better ideas? |
|
That's a good idea. I'd need to use inportlib_metadata to get the path to the module, but I've done that before.
I don't think it changes anything because it's not just the different scheme, but that fact that importlib_metadata version is fixed at install time, even for editable installs, that breaks my code. But your above suggestion should work around that. There will still be se rare combinations of test PyPI installs and editable install versions that might not play nice with this PR, but that's impossible to fix I think due to the historical use of the older setuptools_scm version. |
P.S. I'm not going to be able to take a look at this for a few days so you are both welcome to push appropriate changes if you want to try something sooner |
Yes, I think I have a better idea. The issue with my earlier suggestion is the fact that there is a circular import and new A solution is to define them ahead of time, but then throw them out and replace them with the ones from labscript_devices if it turns out labscript_devices is old: from ._device_registry import *
# Backwards compatibility for labscript-devices < 3.1. If labscript_devices defines the
# device registry as well, undo the above import and use the contents of
# labscript_devices instead. The above import must be done first so that the names are
# available to labscript_devices during the below import, since as of 3.1 it imports
# this module as well.
try:
from labscript_devices import ClassRegister
if ClassRegister.__module__ == 'labscript_devices':
for name in _device_registry.__all__:
del globals()[name]
from labscript_devices import *
except ImportError:
pass for this to work we should define an |
Instead of version checking. Work around the circularity of importing new labscript_devices (which imports labscript_utils.device_registry) by unconditionally importing the new module first, rolling it back if it turns out labscript_devices is old and defines the device registry itself.
I've pushed my suggested change. Tested and it appears to work. Even if you prefer an explicit version check, the same idea of unconditionally importing the new module would work to allow you to import labscript_devices and look at its For what it's worth, the fact that we can't relocate a module trivially and have had to think hard about this is a point against having separate repos. I'm not saying we shouldn't have separate repos, but just pointing out that this is one of the downsides of the choice we made. It's usually not as bad as this though. |
Going to be bold and merge this and the corresponding labscript_devices PR. |
The labscript_devices import machinery has now been relocated to labscript_utils as the first step in breaking the circular dependency between labscript-devices and blacs packages (see labscript-suite/labscript-devices#59).
The code in _import_machinery.py is equivalent to that in labscript_devices/init.py exept that
__name__
has been replaced with'labscript_devices'
.There is also some code that chooses whether to use the old labscript_devices code or the new labscript_utils code depending on the version of labscript_devices. The specific version will need to be adjusted to match whatever version removes the code from labscript_devices (and even then, would not catch the case of users with local changes in a development install) so this may not be the best approach, but I couldn't think of another way to ensure compatibility between old and new labscript-suite components without reintroducing a different circular dependency.
The labscript_devices
__init__.py
file can then be replaced withfrom labscript_utils.labscript_devices import *
so as not to need to update every device class file.