{"action":"created","issue":{"url":"https://api.github.com/repos/pyinstaller/pyinstaller/issues/1393","labels_url":"https://api.github.com/repos/pyinstaller/pyinstaller/issues/1393/labels{/name}","comments_url":"https://api.github.com/repos/pyinstaller/pyinstaller/issues/1393/comments","events_url":"https://api.github.com/repos/pyinstaller/pyinstaller/issues/1393/events","html_url":"https://github.com/pyinstaller/pyinstaller/pull/1393","id":99871717,"number":1393,"title":"Module Import Ordering Preserved","user":{"login":"leycec","id":217028,"avatar_url":"https://avatars.githubusercontent.com/u/217028?v=3","gravatar_id":"","url":"https://api.github.com/users/leycec","html_url":"https://github.com/leycec","followers_url":"https://api.github.com/users/leycec/followers","following_url":"https://api.github.com/users/leycec/following{/other_user}","gists_url":"https://api.github.com/users/leycec/gists{/gist_id}","starred_url":"https://api.github.com/users/leycec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leycec/subscriptions","organizations_url":"https://api.github.com/users/leycec/orgs","repos_url":"https://api.github.com/users/leycec/repos","events_url":"https://api.github.com/users/leycec/events{/privacy}","received_events_url":"https://api.github.com/users/leycec/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/pyinstaller/pyinstaller/labels/@high","name":"@high","color":"e11d21"},{"url":"https://api.github.com/repos/pyinstaller/pyinstaller/labels/PY3","name":"PY3","color":"fbca04"}],"state":"open","locked":false,"assignee":{"login":"htgoebel","id":784161,"avatar_url":"https://avatars.githubusercontent.com/u/784161?v=3","gravatar_id":"","url":"https://api.github.com/users/htgoebel","html_url":"https://github.com/htgoebel","followers_url":"https://api.github.com/users/htgoebel/followers","following_url":"https://api.github.com/users/htgoebel/following{/other_user}","gists_url":"https://api.github.com/users/htgoebel/gists{/gist_id}","starred_url":"https://api.github.com/users/htgoebel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/htgoebel/subscriptions","organizations_url":"https://api.github.com/users/htgoebel/orgs","repos_url":"https://api.github.com/users/htgoebel/repos","events_url":"https://api.github.com/users/htgoebel/events{/privacy}","received_events_url":"https://api.github.com/users/htgoebel/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/pyinstaller/pyinstaller/milestones/9","html_url":"https://github.com/pyinstaller/pyinstaller/milestones/PyInstaller%203.0","labels_url":"https://api.github.com/repos/pyinstaller/pyinstaller/milestones/9/labels","id":831132,"number":9,"title":"PyInstaller 3.0","description":" * '''Add Python 3 support'''\r\n * '''Use modulegraph library for dependency analysis.'''\r\n * !PySide support (should be very similar to !PyQt)\r\n * Update manual and explain better how !PyInstaller works internally.","creator":{"login":"pyinstaller-tickets-migration","id":5377465,"avatar_url":"https://avatars.githubusercontent.com/u/5377465?v=3","gravatar_id":"","url":"https://api.github.com/users/pyinstaller-tickets-migration","html_url":"https://github.com/pyinstaller-tickets-migration","followers_url":"https://api.github.com/users/pyinstaller-tickets-migration/followers","following_url":"https://api.github.com/users/pyinstaller-tickets-migration/following{/other_user}","gists_url":"https://api.github.com/users/pyinstaller-tickets-migration/gists{/gist_id}","starred_url":"https://api.github.com/users/pyinstaller-tickets-migration/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pyinstaller-tickets-migration/subscriptions","organizations_url":"https://api.github.com/users/pyinstaller-tickets-migration/orgs","repos_url":"https://api.github.com/users/pyinstaller-tickets-migration/repos","events_url":"https://api.github.com/users/pyinstaller-tickets-migration/events{/privacy}","received_events_url":"https://api.github.com/users/pyinstaller-tickets-migration/received_events","type":"User","site_admin":false},"open_issues":36,"closed_issues":66,"state":"open","created_at":"2014-10-18T13:32:07Z","updated_at":"2015-08-24T20:55:56Z","due_on":"2015-08-30T22:00:00Z","closed_at":null},"comments":13,"created_at":"2015-08-09T07:04:16Z","updated_at":"2015-08-26T04:41:56Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/pyinstaller/pyinstaller/pulls/1393","html_url":"https://github.com/pyinstaller/pyinstaller/pull/1393","diff_url":"https://github.com/pyinstaller/pyinstaller/pull/1393.diff","patch_url":"https://github.com/pyinstaller/pyinstaller/pull/1393.patch"},"body":"This pull request preserves the order in which modules are imported by the user application. Because that is both needful and good.\r\n\r\nSpecifically, 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.)\r\n\r\nThis low-level machinery partially fixes #1367. Hooks may now test the order in which two hypothetical modules `foo` and `bar` were initially imported as follows:\r\n\r\n```\r\nfrom PyInstaller.utils.hooks.hookutils import logger\r\n\r\ndef hook(mod):\r\n    if mod.graph.findNode('foo').order < mod.graph.findNode('bar').order:\r\n        logger.debug('Module \"foo\" imported before module \"bar\". Why? I dunno.')\r\n    else:\r\n        logger.debug('Module \"bar\" imported before module \"foo\". Suck it, you.')\r\n\r\n    return mod\r\n```\r\n\r\n## What About #1367? Why You No Fix, Huh?\r\n\r\nWould someone (...hey, no names: @htgoebel) mind posting a minimal-length example (MLE) demonstrating the exact issue of #1367?\r\n\r\nWhile 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.\r\n\r\n## What Else? There's Always Something Else\r\n\r\nwxPython's Python 2 version (referred to as \"Classic\") and Python 3 version (referred to as \"Phoenix\") appear to be [_very_ dissimilar](http://wxpython.org/Phoenix/docs/html/MigrationGuide.html#the-wx-namespace-and-submodules-reorganized) – 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.\r\n\r\nThus [spaketh Walt](http://www.bartleby.com/142/20.html)."},"comment":{"url":"https://api.github.com/repos/pyinstaller/pyinstaller/issues/comments/134829029","html_url":"https://github.com/pyinstaller/pyinstaller/pull/1393#issuecomment-134829029","issue_url":"https://api.github.com/repos/pyinstaller/pyinstaller/issues/1393","id":134829029,"user":{"login":"leycec","id":217028,"avatar_url":"https://avatars.githubusercontent.com/u/217028?v=3","gravatar_id":"","url":"https://api.github.com/users/leycec","html_url":"https://github.com/leycec","followers_url":"https://api.github.com/users/leycec/followers","following_url":"https://api.github.com/users/leycec/following{/other_user}","gists_url":"https://api.github.com/users/leycec/gists{/gist_id}","starred_url":"https://api.github.com/users/leycec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leycec/subscriptions","organizations_url":"https://api.github.com/users/leycec/orgs","repos_url":"https://api.github.com/users/leycec/repos","events_url":"https://api.github.com/users/leycec/events{/privacy}","received_events_url":"https://api.github.com/users/leycec/received_events","type":"User","site_admin":false},"created_at":"2015-08-26T04:41:55Z","updated_at":"2015-08-26T04:41:55Z","body":"Thanks for dredging that up. :put_litter_in_its_place: \r\n\r\n**\\<rant\\>**\r\n\r\nI must admit that I don't understand his rationale. The signature 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 `throws` clause for that method. While most dynamic languages including Python are more implicit, the types of exceptions raised by public methods _cannot_ be changed without breaking backward compatibility. **Period.**\r\n\r\nIn 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.\r\n\r\nI'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 `ModuleGraph._scan_bytecode()` method (which parses all `import` statements and is hence the core of `ModuleGraph`) _already_ calls the `_safe_import_hook()` method (which converts `ImportError` exceptions raised by `import_hook()` into `MissingModule` nodes). So, there's no value added here.\r\n\r\nMoreover, this change is irrationally non-orthogonal (..._which is almost worse_). The `import_hook()` method internally calls two private methods to do its dirty work: `_find_head_package()` and `_load_tail()`. Here's a simple example of how this works. Given an import statement resembling `from wx.pub import pubsub`, `import_hook()` would first call `_find_head_package()` to import the parent package `wx.pub` and then call `_load_tail()` to import the child module `pubsub`. **Makes sense.**\r\n\r\nThen things get weird. `_find_head_package()` still raises `ImportError` exceptions on parent packages not being found. That hasn't changed. But `_load_tail()` now silently adds `MissingModule` nodes to the graph on child modules not being found rather than raising `ImportError` exceptions. Does that make any sense? **No.** If one of these private methods raises exceptions, so should the other; likewise, if one of these private methods adds `MissingModule` nodes, so should the other.\r\n\r\nOf course, _neither_ of these private methods should add `MissingModule` nodes – because `_safe_import_hook()` already does so.\r\n\r\nIf you want to break backward compatibility, you need a better reason than hand-waving.\r\n\r\n**\\<\\\\rant\\>**"}}