{"id":238,"date":"2009-04-03T09:52:40","date_gmt":"2009-04-03T08:52:40","guid":{"rendered":"http:\/\/fiber-space.de\/wordpress\/?p=238"},"modified":"2009-04-03T09:52:40","modified_gmt":"2009-04-03T08:52:40","slug":"broken-beyond-repair-pythons-import-system","status":"publish","type":"post","link":"http:\/\/fiber-space.de\/wordpress\/2009\/04\/03\/broken-beyond-repair-pythons-import-system\/","title":{"rendered":"Broken beyond repair &#8211; Pythons import system"},"content":{"rendered":"<p>It often happens that language features we have once distasted become comfortable after a while. We learn to live with them, take their advantages and forget about their annoyance. For many Python developers things like the explicit `self` in the argument list of a method is among them or `len` as a function. Experienced Python programmers are usually weakly excited about alternative proposals and the longer this quirk is discussed their mood turns towards -0 which is encoded as &#8220;why bothering?&#8221;. <\/p>\n<p>Other features are the cause of continuous struggle and we never get used to them but don&#8217;t talk much about them because they aren&#8217;t low hanging fruits. They might even turn from bad to worse in the course of improving them in some respects. Those features feel like being <span style=\"font-style: italic;\">broken beyond repair<\/span> which means that it&#8217;s not easy to patch them but they need a fundamental redesign. In my opinion Pythons import machinery has this quality and it was regrettably forgotten in the many &#8220;cleanups&#8221; of Python 3.<\/p>\n<p>Pythons import machinery is complicated to understand but this alone isn&#8217;t an argument against it. Many systems are far more complex but they are well thought out. The import machinery seems to be designed by implementation and cobbled together. This has finally lead to accidental design artifacts like the module\/script distinction which makes sense only in the light of Pythons import implementation.<\/p>\n<p>Python always suffered from the problem that two modules `M1.py` and `M2.py` which import a third module `M3.py` from different import paths also receive different module objects `<M3>`. This is as if we gave up coordinate independence but believed as we are moving through the space all objects are changing continuously. The objects themselves not their perspective of them. We really get different objects and we can&#8217;t compare our data with each other.<\/p>\n<p>Not only are module objects path dependent but the success of a module import may depend on the liveliness of other modules. Suppose `M1.py` defines the following relative import: `from . import M2`. This import fails if the package `P` containing both `M1.py` and `M2.py` has not been imported already. When running `M1.py` from the command line it&#8217;s not relevant that we declared `P` as a package by placing an `__init__.py` file into it we still get the increasingly famous error message:<\/p>\n<p><span style=\"color: #ff0000;\">ValueError: Attempted relative import in non-package<\/span><\/p>\n<p>This is not a Monty Python skit. The package exists and exists not. The interpreter doesn&#8217;t make any use of the availability of package `P` as long as it hasn&#8217;t been imported explicitly. But why does it has to be imported only to find `M2.py`? There is no obvious answer. It&#8217;s just the way it is implemented.<\/p>\n<p>&#8220;We <em>should<\/em> forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. (Donald E. Knuth)<strong>&#8220;<\/strong><\/p>\n<p>Back to the first problem. Once `M1.py` has imported `M3.py` the `<M3>` will be accessible on M1&#8217;s import path in the module cache `sys.modules` which is a flat dictionary of `{module-path: <module>}` pairs. The module `M2.py`uses a different import path and a new and different module `<M3>` is created which is cached using another module-path.<\/p>\n<p>This lookup with a module-path string is fast and it is usually faster than ordinary attribute lookup in objects which has to jump through a nested dictionary. <i>I do think that&#8217;s basically it<\/i>. If we implement a proper module cache providing the moderate performance of attribute lookups in objects we can get rid of the tedious import machinery that Python has right now. But I&#8217;d like to go even a little further and claim that a whole import system can be designed directly from a tiny set of objects and their properties. No Finders, Loaders, Importers and all the clunky stuff which pretends to be objects but is actually a service which serves the void.<\/p>\n<p>I&#8217;ll flesh this idea out in a series of articles on this blog soon. This can be understood then as the constructive part of my criticism.<\/p>\n<p><span style=\"font-style: italic;\">Newsflash: chances are that higher Python officers will succeed in confusing the situation even more. <a href=\"http:\/\/mail.python.org\/pipermail\/python-dev\/2009-April\/088073.html\">PEP 382<\/a> is on the way. Good luck with explaining why Python needs two notions of packages whereas it is difficult enough to justify even one. It&#8217;s not a coincidence though that this is placed in the context of setuptools. When Pythons import system is a tragedy than distutils and the layers above are the accompanying <a href=\"http:\/\/cournape.wordpress.com\/2009\/04\/01\/python-packaging-a-few-observations-cabal-for-a-solution\/\">farce<\/a>.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>It often happens that language features we have once distasted become comfortable after a while. We learn to live with them, take their advantages and forget about their annoyance. For many Python developers things like the explicit `self` in the &hellip; <a href=\"http:\/\/fiber-space.de\/wordpress\/2009\/04\/03\/broken-beyond-repair-pythons-import-system\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[],"_links":{"self":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts\/238"}],"collection":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/comments?post=238"}],"version-history":[{"count":6,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts\/238\/revisions"}],"predecessor-version":[{"id":244,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts\/238\/revisions\/244"}],"wp:attachment":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/media?parent=238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/categories?post=238"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/tags?post=238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}