Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Closed
jimmo wants to merge 2 commits into micropython:master from jimmo:unittest-move-back

Conversation

@jimmo
Copy link
Member

@jimmo jimmo commented Sep 6, 2022

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.

mattytrentini reacted with thumbs up emoji
Copy link
Contributor

Makes sense, looks good!

@@ -0,0 +1,3 @@
metadata(version="0.9.0")

module("unittest.py")
Copy link
Member

@dpgeorge dpgeorge Sep 6, 2022

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?

Copy link
Member Author

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.

Copy link

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..

Copy link
Contributor

@andrewleech andrewleech Sep 6, 2022

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

options.defaults(peripheral=True, server=True)

Hopefully can use that to modify dependencies too?

Copy link
Contributor

@andrewleech andrewleech Sep 6, 2022

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.

Copy link
Member

@dpgeorge dpgeorge Sep 7, 2022

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.

Copy link
Contributor

@andrewleech andrewleech Sep 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this look @jimmo - do the options need to be declared anywhere? #530

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>
@jimmo jimmo force-pushed the unittest-move-back branch 2 times, most recently from e493cae to 08e535f Compare September 9, 2022 15:22
Copy link
Member Author

jimmo commented Sep 9, 2022

@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 to if __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 (assuming unittest is available in path or frozen)

Or you can optionally install unittest-discover in which case you can do

  • micropython -m unittest or micropython -m unittest discover --args to 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.

Copy link
Member Author

jimmo commented Sep 9, 2022
edited
Loading

Two other notes:

  • unittest_discover doesn't work on unix-standard (fnmatch needs 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 provided TestLoader.discover(). I guess in theory you could construct sys.argv and call unittest.main() though?

Copy link
Contributor

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...

Copy link
Contributor

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.

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).

Copy link
Member Author

jimmo commented Sep 12, 2022

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link

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'

Copy link

stinos commented Sep 12, 2022

Replacing \ with / in _dirname_filename_no_ext is missing so paths with backslashes will be treated differently.

Copy link
Member Author

jimmo commented Sep 12, 2022

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__".

@jimmo jimmo force-pushed the unittest-move-back branch 2 times, most recently from 9ed86c1 to 92b4340 Compare September 12, 2022 12:22
Copy link

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.

Copy link
Member Author

jimmo commented Sep 12, 2022

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>
Copy link
Member Author

jimmo commented Sep 12, 2022

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.

Copy link

stinos commented Sep 12, 2022

Ok thanks, this works fine now for everything I tried with it.

Copy link
Member Author

jimmo commented Sep 12, 2022

Thanks! Merged in 796a598

@jimmo jimmo closed this Sep 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@andrewleech andrewleech andrewleech left review comments

@dpgeorge dpgeorge dpgeorge left review comments

+1 more reviewer

@stinos stinos stinos left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /