-
Notifications
You must be signed in to change notification settings - Fork 1.1k
python-stdlib/unittest: Move back from unix-ffi. #527
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
Conversation
Makes sense, looks good!
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.
I guess there's no way to specify that only unittest_discover.py requires argparse/fnmatch.
Maybe instead unittest_discover.py could be a separate module/package?
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.
I guess so... I don't really know how people use this package.
The CPython way is python -m unittest discover (or just python -m unittest). So if we moved it into unix-ffi/unitest_discover then you'd have to write micropython -m unittest_discover.
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.
How about wrapping the import in a try/catch so that normal usage keeps on working, and discovery is just unavailable unless argparse and fnmatch are available? It's a bit lying about dependencies of course..
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.
Yeah I'd prefer to not need to move it to a different package certainly! discover was split to the separate file with the intention of being able to install it or not depending on whether it needed to be used...
I'm hoping this issue could be resolved with manifest options like
Hopefully can use that to modify dependencies too?
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.
How about wrapping the import in a try/catch so that normal usage keeps on working, and discovery is just unavailable unless argparse and fnmatch are available? It's a bit lying about dependencies of course..
I put the import in the discover function so that normal functionality should work without the unittest_discover.py file... but perhaps an explicit try/catch with helpful user error would be better.
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.
but perhaps an explicit try/catch with helpful user error would be better.
No I don't think that's necessary. The code is good as-is.
The issue is really just about dependencies, that you'd need to explicitly install argparse and fnmatch on unix to use the discover() function. Maybe fixing/improving that can be left until later.
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.
Although this primarily makes sense for the unix port, there's nothing preventing it being used on any port, and it's written for MicroPython. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
e493cae to
08e535f
Compare
@andrewleech @stinos I have updated this in a way that keeps it minimal for non-unix ports, but makes it possible to retain the full discovery functionality for unix/windows.
I've also (I think) addressed @stinos's fixes from #526 and some other bugs too.
The basic idea is that for non-unix ports, you can either:
- Copy the test script to the board and run
>>> unittest.main("test_foo.py")or>>> import test_foo; unittest.main(test_foo) - Run the test script via
mpremote run test_foo.py, assuming it has a call toif __name__=="__main__": unittest.main()at the end.
(I lost track a bit, but I think both of these use cases were currently broken)
On unix, you can run a test script directly
micropython test_foo.py(assumingunittestis available in path or frozen)
Or you can optionally install unittest-discover in which case you can do
micropython -m unittestormicropython -m unittest discover --argsto do discovery.micropython -m unitest [modulename or filename]*to run individual modules or files.
Practically what this means is you can either require("unittest") or require("unittest-discover") (the latter includes the former), and soon upip.install() either.
We're introducing a new convention here that a micropython-lib module with a hyphen is an extension to another module. This will replace the options feature that was previously only used by aioble, and I plan to split aioble into aioble-core, aioble-peripheral, aioble-central, etc and then have an aioble that requires the default set.
Two other notes:
unittest_discoverdoesn't work on unix-standard (fnmatchneeds re.sub). See unix: Refactor variant config and enable features in standard build. micropython#9060 to address this.- There's no way to invoke discover on non-unix (simply because there's no entry point that isn't via
__main__). I don't think this exists in CPython either? We could provide some sort of convenience wrapper, but it would be non-standard, unless we providedTestLoader.discover(). I guess in theory you could constructsys.argvand callunittest.main()though?
In case you hadn't seen the link @jimmo there was another discussion thread at https://github.com/micropython/micropython-lib/pull/488/files#r963787097 which wasn't entirely resolved either...
We're introducing a new convention here that a micropython-lib module with a hyphen is an extension to another module. This will replace the
optionsfeature that was previously only used byaioble, and I plan to split aioble intoaioble-core,aioble-peripheral,aioble-central, etc and then have anaioblethat requires the default set.
Hmm... I'm not entirely convinced on hypen-as-extension; in the micropython packaging world the hyphen is standard/expected replacement for underscore in the package name. Overloading that here is likely to be confusing I feel.
The . is already the standard for extending packages... if they're intended to install into the same namespace then that should be the way it works. If instead they all install into separate folders / files that need to be supported / imported by the base package (needing a list of try/import/except?) then that's a new pattern that possibly needs some more thought... or maybe not as it's basic standard python at that point that doesn't really even need a convention.
I personally like the options system, it's very powerful while still being quite readable (so far).
In case you hadn't seen the link @jimmo there was another discussion thread at https://github.com/micropython/micropython-lib/pull/488/files#r963787097 which wasn't entirely resolved either...
Yes I did see that. I think this PR sorts out the issues raised there (and in the other PRs).
The . is already the standard for extending packages...
Where is that standard? I don't think I've ever seen a real pypi package with a "." in the name.
The idea here is that unittest-discover is an "extension" for unittest. The hyphen was chosen because if it were an underscore then it would appear like the name of an actual importable module. This isn't uncommon on pypi i.e. foo-bar adds optional bar functionality for foo.
That said, there isn't really a great precedent from Python where the extension packages directly modify the behaviour of an existing package. One example is when you use extras_require, which allows you to bring in extra dependencies (which may result in a library directly gaining extra features). So you'd have aioble[server] but server still needs to resolve into a package that can be installed (i.e. aioble-server).
I personally like the options system, it's very powerful while still being quite readable (so far).
Me too. I thought it was a neat way to make freezing simpler. But it doesn't work for installable packages, only for manifests.
Me too. I thought it was a neat way to make freezing simpler. But it doesn't work for installable packages, only for manifests.
I kind of through the syntax aioble[server] should be parsable by upip to install options? I guess that doesn't cover turning off options that are on be default.
Where is that standard? I don't think I've ever seen a real pypi package with a "." in the name.
Good point... I was thinking of namespace packages but no, they don't put . in the name, they just install into a "shared" package folder.
stinos
commented
Sep 12, 2022
I didn't test everything, but the core issue is still there: when passing a module to unittest.main() it gets ignored when the unittest_discover module is installed. The logic I added ('if module passed to main then skip discovery and just use that module') should remain intact (also for CPython-compatibility), and I'd expand that to 'if module passed to main then don't even try to import unittest_discover'
stinos
commented
Sep 12, 2022
Replacing \ with / in _dirname_filename_no_ext is missing so paths with backslashes will be treated differently.
08e535f to
a44b02b
Compare
Thanks @stinos !
Replacing
\with/in _dirname_filename_no_ext is missing so paths with backslashes will be treated differently.
I misunderstood what your change was doing here. Or perhaps I don't understand how paths work on Windows and how they interact with sys.path?
When the user does
$ micropython -m unittest windows\path\to\foo.py
then should "windows\path\to" should be added to sys.path, followed by import foo? Or should we be adding "windows/path/to" to sys.path?
I've changed it to the latter because it sounds from your comment like that's what it should be.
when passing a module to unittest.main() it gets ignored when the unittest_discover module is installed.
Ah yes, of course. I've fixed this such that unittest.main() never uses discover. You now only get discover via __name__=="__main__".
9ed86c1 to
92b4340
Compare
stinos
commented
Sep 12, 2022
then should "windows\path\to" should be added to sys.path, followed by import foo? Or should we be adding "windows/path/to" to sys.path?
Ah, that doesn't actually matter, both should be handled fine, the issue is that _dirname_filename_no_ext('path\to\foo.py') yields ('', 'path\to\foo') because it tries to split on / not \. So it just adds an empty directory to sys.path.
Ah, that doesn't actually matter, both should be handled fine, the issue is that
_dirname_filename_no_ext('path\to\foo.py')yields('', 'path\to\foo')because it tries to split on/not\. So it just adds an empty directory to sys.path.
Oh!! I thought i was explicitly handling that by using os.sep. That was the whole point of why I changed that. But now I see that on the Windows port, os.sep is stil /. That needs to be fixed in the Windows port right?
In order to make this more suitable for non-unix ports, the discovery functionality is moved to a separate 'extension' module which can be optionally installed. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
92b4340 to
be93747
Compare
Updated to just use the replacement, with a comment explaining why. We should implement sep correctly for Windows, as well as altsep on Unix/Windows, and can fix this when that's done.
stinos
commented
Sep 12, 2022
Ok thanks, this works fine now for everything I tried with it.
Thanks! Merged in 796a598
This was mistakenly moved to unix-ffi because it was marked as having a
dependency on unix-ffi/argparse, but this is actually only a dependency
for the "unitest_discover.py" script.