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
Module Import Ordering Preserved #1393
Conversation
|
@leycec You may again put me on some pedestal or call me names, but have you ever though about making modulegraph call our import-hooks just when it imports the module? There already is some method you may overwrite: Actually this method seams to import the submodules, too, so you have to test this in detail. In the best case, you can call our import-hooks prior to calling this method. In the worst case you need to re-implement this method and call out hooks in the midst of the existing code. If everything fails, and we need to ad some |
O.K. There's an existing unit test. You're awesome. I'll get on that rickety horse right away.
Too late. See "You're awesome" above.
I've definitely thought about that. I assumed this was not originally done for reasons that escape me. I may have assumed incorrectly. Your wise and seemly suggestion may be the perfect long-term solution. For the lazy moment, however, I have two concerns:
Absolutely! Why must I forget the essentials? //ruefully shakes head//
[DANGER: Big wall of text follows. Proceed with ninja-like caution.] I'm beginning to come to an unpopular opinion. No one's going to like it. Rebel without a cause time. In my humble opinion, PyInstaller should internally fork Yeah. I just dropped the fork bomb. Why? A few reasons (in no particular order):
|
@leycec Forking modulgraph may be an option - I was thinking about this, too. We should discuss in a separate issue. Could you please move the text from above into a new issue? Thanks. Regarding the topic :-) Thanks for you analysis here. So we need to handle the order in
The main differences to you implementation is:
Re. 3.: Anyway I'd like to keep your code for the time we fork modulegraph. |
I am delighted to comply with your always sensible advice. Issue #1412, "To Fork or Not to Fork Let the vitriolic discussion commence! Bring it.
Ah-ha! I was hoping no one would think of that. In the ideal world of my grandiose dreams (...I am a flying king there), that would work. Unfortunately for you and I both, the definition of the parent
Under your proposed At this point, you are complaining: "Yes, yes. But isn't that pure conjecture? Does The codebase is quite convoluted. (Understatement.) While it's difficult to say, the But this is all beside the point. The We should definitely preserve idempotency here... if only for future sanity. And the only sane way to do that is to initialize the The insane alternative is to refactor your proposed
It's incredibly awkward. It's also a little inefficient, as the
Shitballs of fire. You're absolutely right. I'll refactor the current request to increment a new Superb catch. ⚾ |
Updated. The entire pull request has been truncated to only three lines of code, ignoring the usual overly verbose documentation. This addresses htgoebel's cutting observation that the prior approach broke under node deletion. (That's bad.) The new approach guarantees node ordering to increase monotonically, regardless of the current number of graph nodes. (That's good.) Next up: fix wxPython. Weeeeeee. 🏇 |
Indeed it is :-( I'm fine with you change, I'm going to merge it. Thanks for your thorough investigation and description. Only one thing: Now we have |
I've been hacking on this for quite a bit. It's a complete cock-up. Specifically, nothing in the codebase appears to work as expected. The I'm fairly confident that none of these things ever worked. They couldn't have, for reasons I am currently too contumely to rant about. Long Story ShortThe PyInstaller codebase is mildly frustrating at the moment. It's no one's fault; everyone's hacking hard; all's right with the world. I will get this working, but it's going to take blood, sweat, and crocodile tears. Expect a big fat load of commits sometime... soonish. |
Oh, fuck me. Has someone been hacking on our embedded Specifically, the
That's not good. Everything depends on that method raising an exception. Everything. Like, for example, the This is exactly why
I don't quite know what's going on here. But it's bad, and I'm definitely going to spelunk to the bottom of this. |
I don't get it. Insanely, Ron himself appears to have made that breaking change. After locally reverting back to the older working behaviour, I now receive the expected warning on missing hidden imports: e.g.,
Which is good. But Ron broke that, which is bad. In my (possibly not-so-humble opinion), we should immediately stop pulling upstream changes from either |
For the records: Related change, done by Ron:https://bitbucket.org/ronaldoussoren/modulegraph/commits/cd6865045087e94bf38bb0dd2f0aae9421ed6632:
|
Thanks for dredging that up, man! Your
I must admit I don't understand his rationale. The contract of a public method comprises its method name, return type, argument types and order, and (crucially) types of raised exceptions. Java is probably the most explicit language in this regard, requiring all exceptions a method might throw be explicitly listed in the In this case, a core public method was changed from raising exceptions on fatal error conditions to raising no exceptions at all – a complete rupture in the space-time continuum. I'd probably be more agreeable to this change (...despite it breaking PyInstaller and wasting several hours of my fragile time) if it actually made sense. But it doesn't. The Moreover, this change is irrationally non-orthogonal (...which is almost worse). To see why, note that Then things get weird. So it's non-orthogonal. Does that matter? Yes, but only if you value sanity and a non-broken codebase. Here's why. On import errors, I note at least six calls in PyInstaller and If Ron wants to break backward compatibility as well as his own codebase, he needs a better reason than hand-waving. <\rant> |
There's good news, and there's bad news. The good news is that I've fixed both #1367 ( The bad news is that it cost me precious sanity... and I was already running on empty. 😱 Additionally:
PyInstaller and its So, here's the plan. Since #1367 also requires the module ordering change implemented by this pull request, I'm going to begin collecting all changes required by both #1367 and #1420 here – starting tomorrow. These changes span The anticipation in the air is palpable. |
Will you ever get sanity back and how long will it take? Have you run into debts when running on empty? |
Nattering from the peanut gallery...
|
For the sake of release 3.0 I'm willed to accept (nearly) any code solving the blockers #1367 (wx.lib.pubsub), #1420 (venv-specific distutils) and #1322 (extend @leycec I'm eager to see the code :-)
|
There will be doc changes needed. I can take those on. Please be thinking of notes that will help me explain the uses and conventions for the two kinds of hooks. Maybe there should be a separate issue for doc changes for PyI 3.0? Then notes can be dropped there, and I can ask questions there? |
@leycec Rethinking your statement, which sounds like you are making the Earth turn around the other direction: Is there any chance, to implement only a hack for now? E.g. by overwriting |
This method replaces the undocumented spaghetti code of the modulegraph.find_module() function with well-documented and maintainable code. Refactoring this function into a method permits this method to be subclassed by the "PyiModuleGraph" class and hence hooked for retargeting modules.
Spaghetti code in this method has been marginally improved. Open file handles are now guaranteeably closed on exceptions. Terse and ambiguous variable names (e.g., "m", "fp", "name") now have human-readable names. For orthogonality with the ModuleGraph._safe_import_hook() method, the ModuleGraph._import_module() method has been renamed to _safe_import_module(). All methods squelching "ImportError" exceptions should be prefixed by "_safe".
For disambiguity, "graph hooks" are now named "pre-safe import module hooks" and live at "PyInstaller.hooks.pre_safe_import_module". The current "hook-six.moves" hook has been refactored to support the new "PreSafeImportModuleAPI" class.
PyiModuleGraph now overrides the superclass _find_module_path() method with support for pre-find module path hooks. In keeping with superclass changes, the _import_module() method has been renamed to _safe_import_module() and now supports pre-safe import module hooks with a proper communication API. PyiModuleGraph now accepts an optional list of third-party hook directories on initialization, permitting users to hook these new hook types.
The "building.imphook.HooksCache" class has been refactored to map from each module name to the list of the absolute paths of all hooks specific to that module name. Previously, this class only mapped from each module name to a single absolute path, ensuring that user hooks override official hooks. This class now subclasses "dict" rather than "UserDict", which has been (mostly) deprecated since Python 2.2. For convenience, tilde and variable expansion is also now applied to all passed hook dirnames.
The PyInstaller.building.build_main.Analysis.assemble() method now runs all available post-graph hooks for each hooked module (rather than merely the most recently cached such hook for that module).
A convenience @xfail decorator for pytest tests has been added.
Excellent suggestion. The Django test and newly added wxPython PyPubSub tests have now all been marked as failing. |
The Django test is currently known to fail with "GraphError" exceptions and has now been marked as such. For organization, this test has been moved to a separate script in the new "tests/functional/test_hooks" subdirectory.
The wxPython PyPubSub tests are currently known to fail (due to underlying hooks failing) and have now been marked as such.
@htgoebel If you are fine with this don't hesitate to merge it. |
@leycec Great work! With these changes I can finally build MCEdit using PyInstaller 3.0. |
New hook mechanisms for pre-safe_import_module, pre-find_module_path and post-graph hooks.
@leycec Thanks a lot for the great work! One thing came to my mind: Shouldn't we make the existing (old) "ImportHook" handler submit a API, too? And should we merge the old hook mechanisms into the new ones? |
Glad to be of service! I only regret it taking so long. Also, MCEdit is a wonderful service to man. That is all. |
@htgoebel Excellent questions! You always keep me on my gangrenous toes. 👣
Absolutely. The new Making the switch-over is trivial:
I've made the switch-over locally. So far, so good. I'll probably submit a new pull request finalizing this change over the coming week.
Absolutely. tallforasmurf and codewarrior0 were completely right on this point; I was completely wrong. Thankfully, I've seen the light. (It burns!) What's the Plan, Man?Here's how I see this going down:
What Could Possibly Go Wrong?There are a few things to consider when performing this refactoring, however. For each hooked module,
To ensure each hook function is called at most once, It's not exactly trivial. It's not exactly hard, either. I estimate a week's worth of design, implementation, testing, and documentation. What About Them Hook Functions?Yeah. We'll still need separate hook functions, of course. These are:
After implementing lazy hook loading, we won't need to separate those functions into separate hook files. They'll all be unified into top-level A big win for everybody! (Mostly, tallforasmurf. Best of luck on that documentation. I don't envy you.) |
I moved the discussion o a follow-up-ticket. |
Excellent. Thanks for that. |
For the records: test_django fails with |
This pull request preserves the order in which modules are imported by the user application. Because that is both needful and good.
Specifically, a new public
lib.modulegraph.modulegraph.Node.order
attribute – guaranteed to monotonically increase on every module importation and hence provide a reliable means of comparing importation order – is added. (Tested and working, you may be sure.)This low-level machinery partially fixes #1367. Hooks may now test the order in which two hypothetical modules
foo
andbar
were initially imported as follows:What About #1367? Why You No Fix, Huh?
Would someone (...hey, no names: @htgoebel) mind posting a minimal-length example (MLE) demonstrating the exact issue of #1367?
While I do have the Python 2 version of wxPython locally installed, I'm unsure how to properly replicate this issue. I know; it's probably trivial. I'm feeble-brained, but hard-working. As soon as I get an MLE, I'll append a commit to this pull request fixing #1367 in entirety.
What Else? There's Always Something Else
wxPython's Python 2 version (referred to as "Classic") and Python 3 version (referred to as "Phoenix") appear to be very dissimilar – to the point of actually being different projects that confusingly share the same name. Our import hooks should probably account for these differences in ways I cannot possibly fathom.
Thus spaketh Walt.