--11364638-0-3248466697=:9007 Content-Type: text/plain; Charset=US-ASCII Content-Transfer-Encoding: 7bit Here's a new patch. Changes include: - pyc time stamp checking (thanks Skip and </F>!) - better python -v output - also works when zipimport is built dynamically I've written a note about various aspects of the patch (pasted below) but I'm not sure it's PEP-ready yet. Comments are more than welcome! Just --------------------- This note is in a way an addendum to PEP 273. I fully agree the points and goals of the PEP, except for the sections "Efficiency", "Directory Imports" and "Custom Imports". However, I disagree strongly with much of the design and implementation of PEP 273. This note presents an alternative, with a matching implementation. A brief history of import.c -- digging through its cvs log. When Python was brand new, there were only builtin modules and .py files. Then .pyc support was added and about half a year later support for dynamically loaded C extension was implemented. Then came frozen modules. Then Guido rewrote much of import.c, introducing the filedescr struct {suffix, mode, type}, allowing for some level of (builtin) extension of the import mechanism. This was just before Python 1.1 was released. Since then, the only big change has been package support (in 1997), which added a lot of complexity. (The __import__ hook was quietly added in 1995, it's not even mentioned in the log entry of ceval.c r2.69, I had to do a manual binary search to find it...) All later import extensions were either implemented using the filedescr mechanism and/or hardcoded in find_module and load_module. This ranges from reading byte code from Macintosh resources to Windows registry-based imports. Every single time this involved another test in find_module() and another branch in the load_module() switch. "This has to stop." The PEP 273 implementation. Obviously the PEP 273 implementation has to add *something* to import.c, but it makes a few mistakes: - it's badly factored (for example it adds the full implementation of reading zip files to import.c.) - it adds a questionable new feature: directory caching. The original author claimed this was needed for zip imports, but instead solving the problem locally for zip files the feature is added to the builtin import mechanism as a whole. Although this causes some speedup (especially for network file system imports), this is bad, for several reasons: - it's not strictly *needed* for builtin import - it's not a frequent feature request from users (as far as I know) - it makes import.c even more complicated than it already is (note that I say "complicated", not "complex") - it changes semantics: if a module is added to the file system *after* the directory contents has been cached, it will not be found. This might only be a real issue for an IDE that runs code inside the IDE process, but still. A different approach. An __import__ hook is close to useless for the problem at hand, as it needs to reimplement much of import.c from scratch. This can be witnessed in Guido's old ihooks.py, Greg Stein's imputils.py and Gordon McMillan's iu.py, each of which are failry complex, and not compatible with any other. So we either have to add just another import type to import.c for zip archives, or we can add a more general import hook. Let's assume for a moment we want to do the *former*. The most important goal is for zip file names on sys.path and PYTHONPATH to "just work" -- as if a zip archive is just another directory. So when traversing sys.path, each item must be checked for zip-file-ness, and if it is, the zip file's file index needs to be read so we can determine whether the module being imported is in there. I went for an OO approach, and represent a zip file with an instance of the zipimporter class. Obviously it's quite expensive to read the zip file index again and again, so we have to maintain a cache of zipimporter objects. The most Pythonic approach would be to use a dict, using the sys.path item as the key. This cache could be private to the zip import mechanism, but it makes sense to also cache the fact that a sys.path item is *not* a zip file. A simple solution is to map such a path item to None. By now it makes more sense to have this cache available in import.c. The zipimporter protocol. The zipimporter's constructor takes one argument: a path to a zip archive. It will raise an exception if the file is not found or if it's not a zip file. The import mechanism works in two steps: 1) find the module, 2) if found, load the module. The zipimporter object follows this pattern, it has two methods: find_module(name): Returns None if the module wasn't found, or the zipimporter object itself if it was. load_module(fullname): Load the module, return it (or propagate an exception). The main path traversing loop in import.c will then look like this (omitting the caching mechanics for brevity): def find_module(name, path): if isbuiltin(name): return builtin_filedescr if isfrozen(name): return frozen_filedescr if path is None: path = sys.path for p in sys.path: try: v = zipimporter(p) except ZipImportError: pass else: w = v.find_module(name) if w is not None: return w ...handle builtin file system import... Packages. Paths to subdirectories of the zip archive must also work, on sys.path for one, but most importantly for pkg.__path__. For example: "Archive.zip/distutils/". Such a path will most likely be added *after* "StdLib.zip" has been read (after all, the parent package is *always* loaded before any submodules), so all I need to do is strip the sub path, and look up the bare .zip path in the cache. A *new* zipimporter instance is then created, which references the same (internal, but visible) file directory info as the "bare" zipimporter object. A .prefix contains the sub path: >>> from zipimport import zipimporter >>> z = zipimporter("Archive.zip/distutils") >>> z.archive 'Archive.zip' >>> z.prefix 'distutils/' >>> Beyond zipimport. So there we are, zipimport works, with just a relatively minor impact on import.c. The obvious next question is: what about other import types, whether future needs for the core, or third party needs? It turns out the above approach is *easily* generalized to handle *arbitrary* path-based import hooks. Instead of just checking for zip-ness, it can check a list of candidates (again, caching cruft omitted): def find_module(name, path): if isbuiltin(name): return builtin_filedescr if isfrozen(name): return frozen_filedescr if path is None: path = sys.path for p in sys.path: v = None for hook in sys.import_hooks: try: v = hook(p) except ImportError: pass else: break if v is not None: w = v.find_module(name) if w is not None: return w ...handle builtin file system import... Now, one tiny step further, and we have something that fairly closely mimics Gordon McMillan's iu.py. That tiny step is what Gordon calls the "metapath". It works like this: def find_module(name, path): for v in sys.meta_path: w = v.find_module(name, path) if w is not None: return w # fall through to builtin behavior if isbuiltin(name): return builtin_filedescr [ rest same as above ] An item on sys.meta_path can override *anything*, and does not need an item on sys.path to get invoked. The find_module() method of such an object has an extra argument: path. It is None or parent.__path__. If it's None, sys.path is implied. The Patch. I've modified import.c to support all of the above. Even the path handler cache is exposed: sys.path_importers. (This is what Gordon calls "shadowpath"; I'm not happy with either name, I'm open to suggestions.) The sys.meta_path addition isn't strictly neccesary, but it's a useful feature and I think generalizes and exposes the import mechanism to the maximum of what is possible with the current state of import.c. The protocol details are open to discussion. They are partly based on what's relatively easily doable in import.c. Other than that I've tried to follow common sense as to what is practical for writing import hooks. The patch is not yet complete, especially regarding integration with the imp module: you can't currently use the imp module to invoke any import hook. I have some ideas on how to do this, but I'd like to focus on the basics first. Also: the reload() function is currently not supported. This will be easy to fix later. I *thought* about allowing objects on sys.path (which would then work as an importer object) but for now I've not done it as sys.meta_path makes it somewhat redundant. It would be easy to do, though: it would add another 10 lines or so to import.c. I've tested the zipimporter module both as a builtin and as a shared lib: it works for me in both configurations. But when building it dynamically: it _has_ to be available on sys.path *before* site.py is run. When running from the build dir on unix: add the appropriate build/lib.* dir to your PYTHONPATH and it should work. --11364638-0-3248466697=:9007 Content-Type: application/octet-stream; Name="zipper4.tgz"; X-Mac-Type="00000000"; X-Mac-Creator="00000000" Content-Transfer-Encoding: base64 Content-Disposition: attachment; Filename="zipper4.tgz" H4sIAAAAAAAAA+09+VMbR7P5VforBqUCEixYEhJn7BQxEPM+GyjAuV1bi7SCjSWt vLviSML//vqYc3fFkcTOq+9ZlVjs7ExPT09PX9Mz+j2aTMKk8+yLj/hpNjvN9W4X vpvN9TX+brY7/M2fL6DCerfdWmu11r9otlqrnfYXovsxkVKfaZoFiRBf/AZ/3FcP qg0GnwKhT/v5Xc4/fEejSZxkK71/vI9mq9lc63RmzH9nrd3t5OZ/tdtd/UI0/3FM Sj7/z+f/y2jcG077oagd32aX8XjlslY1ZWmWTHvZKBydh4n7Jk774SB1y0ZBkl4G Q7ewF48m0TB0Cr/OohGUvKhWq0DWLOqJ3mWQLIrJrR/eZOE4jeJx+ss78Vz8UVuZ 3PZqnsDvWH7X7rah5bNFoZk2TER8/lvYywRgFY2jDACIYNwX6XSC78Xis2o1u52E 8FrwoJzGP0eTA/n3NuJUqPBHtXJ8e0Rd+K/2dnbNo1gMkt5ldBVuCwEoTYLschyM QhEPRHYZIhAhKyASVrMBUCWFRtSsH0HRRTgOk2Ao8I2IxoM412KSwOButrkFVeKS LVELnqXT82f9KIGKcXL7rIZN77Y1fY9vz2D0EpA1Wh+Lt00t1ZWuspckMdIEe4yT 6yDpA4l7QwRfaJSEQd/XONRxTjV1GiWdDGOoDwTyR3F/OgzrFl5iMQ2HA08wjMF0 OESietUKF0ADJLQHVMpElE6C3vvgIlSPkuX6nkA+8zMxwm/PJmUW9/xwnCW3Bi9u 66fhhDGHP+Bl9UviqNAh2svLsPe+Hk8aejBER1Xsifk8iRvV4vCrFoP54/C67kzS Ym+YesLmsgvn+f11P21UgS9hZg4uxnESAp9dTEcwKqhng8b1ILLgfZiKXpBIzgwy oAdwEi6S61D04/FCBg9QMRYjqCuG0SAUQIc+TAbMPKyk894wSNMwXSG2LEwW0KqC 37Bq3ZlswEiWX2QTPxgO416dxtUE0laigahTi7nn4vDt69cNXGYEY/mFWjP8ZluX 06oplPI6MMV31UoSZtNkLOqGYg3G8q5qzXg1T6gyJnzEJFiLFPjSVyCh8qKWIRo7 Yq/z6eCXNzs/Hu+cvXq9d7jUeqdeLDJnL+bHhPw5DMdIZiTc3PHtTnLhH4PMDc+m E1g+jFztaG7LGtSKT8Py/RrMdwVnHD7zx7enWRKNL4g3gV0dmjcaUFOSb7mF/R3f +geHL0/29utuRcAKkQUcNcCdlP8o1gTc7Yqn0e9hoRIPjWo+Bx4hfji+BRnkn4aZ BOyKJhiwYhVCJUpFOJpktzXs0h4F8oQC/uK5MKR/VC9Iu4rbUxbHYhiPL2b2VMdq v0B/y613OJ6FyYL480+RLz1eaIj5eVqMwjRp88vIbSJLD0qbrPLL390msvRnq4l5 2eGXKwtMBM1yNMAlYjdSNkRSVJxAGlr9MMRwmIbUCt4rwoyCW3GudR+IjVGVeK72 DCE+y+JnsuYKsCiqLNAWpKoqivdl50hSlDr17W1GrUIqD4QVAl4EQZQtirjXmyZJ MO5RjzWEKYFJaB8AGiAN/9UnnqxAs0Xz8wHHTnKHZjdf+eeD49mVz0HXvaeXH8TS c9HR9aQGWfzQ4IqapB+oykT9dafZRNWwZSAMdhwLxBeIOIVR06iYR/eBqkFWzqDj GIQ42RwoJ7fEVyvtZjMFswlpWsanvCgVDsu6HlBh3Jvc1kFKcWMPmQFYoqUEtxoo N23YDLS0xFyTvgdETveOGXsUeAADrbqFX5sLiAz3Dn3BHxIHQpLkm+yAi7EloNd6 JzuqmGIECJ1su6WIqt0V01tKK0Blu1pQNFo07SfxSIoBTQ9XzZRWVfgbxWZB59nF lemCMkzlSFyg3llyi8r4IsQpBX7uXYZ9xyYdQN8ivU1XXJWzQov8B63T+30Eg8um hzBEPAWNMbxSetxtzGO7Tf3vwow1Wr3m1qgpBsg1lDochAxA2AVzVlpJbjU5fZZO lJWhv4MsHOWqM1vDeArahHlQgbE6L9ppqlbDSJJ0HGS9S7K6U6ajpk9feRIsSFyr o56zbDRoWYWYELTlj466pFcNw4W8jl8OwyCp23aQ7scWBfgSIH4fJudxGu4Pg4sG d4Kz9EMSZeFpBjZaUq99abhjS6AtjsKa3ISaImNNW+c86oWv0oVfx7Z8cMebs+h1 rVkYYz85CSMfm7bhdRVHfcfy6odsGxaML2lc+T/u7pXbH4V3mtiFN0bA8HN87qNL SKbpIAnZGmnkDUQ0hn0Eig7IbBeF3BP5NwlN/nNx4htHBR60r0IDM/aea98Bh/74 448e/kMGOjDnWKQxeJVQsiXGYUhruh9bZgx0CMwu9TMJcC2/HzLOjHUmRTG0wjJL YpOArVbkcJSFQJKU7QSUtw4rSwHnmvYaKdNoNnpKsyhNsVRuPZppvavO6AEnpzBI xczKGDJ6RJUs2RqE1+GbYDLB/l8F6X/CWwddYjy5kni49nwDoNa2IYExsBjDmjbT 7eFubruWVg5gE6va6EtUzaojfrp7yPdEu0r54KYKiMG860NMW1gCRYcPmmpvBow8 XhvSr3H9nneS8SOyLzzjyz/o5qSOl2MNwXF0ZO+2S8PulGTVLIE1kNQ1kmQKOzyb dwzg+abnfx8Mp6FteUkIYjRNM4FmGJjBAazQLAv7K/jGcRO0m6p40pUyLFg0UrSI 55l880aCbBuH5mtwloojJAM6IkYREdRZha+lJWcpGj70ctG36J2W9E9k+5y7yDb0 THdcKUWQeTx/RD1j9Rpg8NdhPKZxS2iy5BEsTqGmf57FSwJKmvF10MpaA0XmL6iA kpVQscNYT1wY1sCdhaGwm7Uy5rSoZSPK1Rhgx0rvs0zYoxlW3l6J9CctLWVDoyaU HQUZtDifZmB60IKDxcbOaTprman1SeKf1ryZH1z0SnepasagMS1Vi20Wyead4/AA NFLBf3NpP1skCit1y+NeUi3FL0vgVGul8Q7ewNqlFkUlR8WoEp9rR32J/LIlAg9q 3bIYbIlAwXZERnM3GexKGkibfaY4kDQ1jR0fgZhWcocJx+oghapAmt56DzOzxuxD y4EQOhhnYDy8jomHuJnyJ3RLT3TYlAChIc1COwis9KwFt8mTio745BbMr2nSAya8 CqJhcD7kaP59EtbIBlh+Ooz3aNELFf8y1cvJLqWzGyNHyRZhjTbLacY1H5lnrjUr htjWxN2Ve4EfO/zOcXfh0HnmBDDW1P9zJdklyqgczoM+LKULEPHjKW5HgUUtzm8z 8NriPvYB4xwDDaXbZnUCEBl8pRePs2g8DenpztJKUEUqooJvpt21ewKQltvVC9Dn RvJJbVaz1BVzgK2r3oTZZdzfDQdOwH5EpXIDrFr5o+ZYN2KG6eaJN3tnr/zvd052 Tr47BbTAV6jdedjeUQJihl6c3R7x9ioV/Loj7zlEOobDwh7TG9ooLA4HS63hSCEE qIgz/+jb/9l7eeaJeDBIwywe2HrWU/Kq4VVO9nZ2jw5f/8QjIoZHAA+DYIcwD4AF KkB4qLlUWbn2RAsa/PHtLnDx6dkJEK7u+LRxz6siEcEK7NEWSnG7smbvMO3u7e+d nOzt+ju7uyd7p6d1/G6I5uM28Zi4ziYlGE4HZ/UC2HmGw/tSoByaMCDDwysWlmgx pCB/XZo0PGxTkesSnOcIxBFWo7VX74e8fxonjRIX35Nt5CM1cYBNEnKV88UXYYZK v/ginfUCxRBudxVeJOGkpHqQ+lKwlL1Kww/TEAPNZS9HLJmL7y6D9LIEMRh5yTjk GPQMfoe7wVEPxP0OjM/LESKeSYmSN4AjSOxBqHrwz473X+98dwoicn/n7esz8aew Cr/dOd07++l4zxPcejAMLlJqmWdv3UG/bCazJLiCdV9Csx6K15KJiXqXM2ftOgze D6M041VafB9lZVOHpVotlMhYMwRZUFKRpJddkQpKWbQUs/OgjAa0418sDdNegpBm vSrtAWFZdKmjVThJ4p6zALFQcxHtCTMzkCyQzLbDS1TzjV6iua1qUwUeXLbdDYee wQzjakpNYA4BABmGuEcdUIoGpWWwczNBRwYYKcXnOsoA+LsfZHJXX74g90zmZ2Qj sHJHtO2cjVayEdCmh+aMrCrmRfOmNWiIRdHe1nVG0diu8+KF6Dao5urAVLoEWy9X q9VqSIAWrH6AppnEs/g6pq7q6r3pC5BapnCQrHmLyyFXdZOrrkPdJbHRNJWjtA8e D5mcQNBrwOHZLSISpSKiPIA+RwCVbfOeCDoPrTm2CY4FaM1cZFc7dssvWGFWsdJ3 UDQWgdrvlFtLsi5agdGwj1EOYD8w9qQUpIgzmokpgqgDBwVDtQOhm05TrIo+SJAC hDSE6Q9QZVBNYDo0C6MwXSE8dowJicMMRIY2/ha9Iy8UpUYSpqnHz18KVYBM9h5s pG3RJBU8Has3YV+1FkDywEcF5snWpMzigTB1qY4AYP0ofa8b4lBLG/bDXFO3Ca9V j5rIdYuNsAve1MmChIokvXRraVCrz5doZFKSC1ZG0LpmL+mZekSRpMch4nQ6ytdu ECV3JTdElCgyRqcaCuKLMQyqD7Y2zSAI9Wio5g7WEpZxao5NSPLVraEKFOnNlSoy ZjFQc2/ekJtdkUv/2D94vQeFE4ztgvcnDB/Q+K2JtabKmQKWMBxYYRiXgA1IOVUB Bycbyjf4UAjKcFsM3lD85gId+l48HWcqxkNxWitcvyS6Ov6Dfcx6tzixgpXo0uul +uLBJAYOqByBFh4M42s7poLrmFY1BRNgTZVmM5hsGstnNUY5xRhxR3sQTwxi4Dkk 53qXEN8Xw6nle9glnhQCtvazazqCVVvgve2Fmo1PCeYwi+F7QMMTy+22h24uqqZT KOS1FgKnyuVAikwFPCmV0T+BKce4Au7z7kMVAETx4eZNc63Z7XzbbeoN+2+D/pbY Y2gvUVaB2IMlBTLhYhwAUgy+MugN45TgbD+FHPmd/adTAkghF4/ccj0Mr/X2Y2Eb 7yIGQRzK5D+biGsehwqcZSIDFLNpRgEtcarkmkUfuR2JtOF1g4oQObGR26eh1SR3 PZzUECsIS7EPqAWIUwTF4J1b1c0GB3c0RjT/XInnafiYMXEAXjJEu9kihsCZ4fwQ zRY2P2Bz8crqaQaSuDHUlJlyFSXYCkidXgJ75LHSgbEHa5K18aiaqD8eQxGjAx5T W4vlR9XWsvhxKFvyGhp01oCiBsISZyY9AOaR1bbvncZOW02jrRIfy14GZUfgI5fZ 9DCviPNRLMtYdSFKqZtZ0coKpV6R7m2A+9FTGBAKiyTEF54tcE4TVdUpN0tLKnZG 5U3i+h3MOgkTsLZB2YNiHE/B56VwHbF8jkLPXdXK8pQiwzodNUUBisYG25P2cgXt 1YuTROdsmIlwND3PwNOXdae5qpe1jiDqlf2a0LlnTdtTviTaazYiq00o+oc4EDFi 2kiy6IiMw3VA6+F2pSD5yPYkt0FvKIMotlKrVEKYiVpf0B9yd92zMxGW1etlShez Ququk2dsL5ZaNDX+t+hV0CZQvRbBZxjVPMuwK7Xpqhx/duZb2sogt0xI3ElUsVVc pQJ/GNV46sTYZXSdx5nlNxE0dISALFMCnHQXLZS76oyMnocSenhH9CtwuMi3Qtv7 qxRzd5gvAb6j/qX2l1lJhMhW1TU+rAQZnTQzM1KtjXUQDr5xb/wBeFR1zOeRnnmu uqlp7YAgAewXrn2omv4+jM6RdvjNuzxECZ+/3vDGRA3f1pj7mVKC6hPfYz/05G6A ODhZ4TaKs8kpx2ZggJmqtfzeBdZQOV1q56iwe/DE7K2Rnm1C28xvxR6G+EbU9DZU DUzBt4c73+8cvN759vVezd3KslLRzFBoScqJNqX3TDduBOGycz0zTyYTo71llifF aswSxUfL9dKPvEhzzl0SXFM/HvANyiMermdzkfaKOIXT8v+k2eeRlkl99CnZWHzY R5lDMVtwTth5OjjSbhPa2q6DQlY5uSgkRL9KHzTB1RDLc0h3xn3a7pSEbvDwNTnB ZmWEDRSbsV3PouAI8QZwMdlKASO2YCl4nwZVku65lHQOuQf4zUlnLVdUE1IOhgrQ n3/aUwY8bsZ7j1NrzUulUvQbCRYCquXEtTXasvlBpLR0mNNnEDBvQ6NFmVY/L8gt /+AazJKI5DFggfTC3WKSP7qFLfltsQfQ6fAXTiWAQG6y4kYEQ6Ko0JbZgbtGhF1H oGi1zHNkW5moVpSfLX8fcQ6iQG4LGmEpM05JOrPrquUVEd7RjkAYvSSkMH4ZDIf7 gC4GiS1U8XTLENaYERTLra5UZXJ+HTGn1F759CsRSGQ1wg99TxJ7+EddSxsiDwV5 bqDtjQB0KZ2n+Y5DugMq/RNsaCxtqdKG+PprITacl23nZWvNebnqvGx3DKI3+dTU 8INPRk5dhqKylj7xlrVJtsonXJpZC8yxrK2mX6WM4avlPrvnKtwdj4d42CMGKooQ I8ApEHXcB/KnMUYFwbqLQrlJp4govqYEx1IdMh3Lk6E+7tXX8+lRJgWKny3rgebY DrTlNAbCs1VCqYBT840aQjuPdh6pln4UZcMaMJjNJ6wHzE2Y3PYsgVMuWTRXET/N WUYNWB9vMLnhkLYg642n535/lYrLIDVpEtp0qAh9KFXJwmJCXT6jjmUbRq6GAvcr wZ/AwHxyK4IhbqnBFMsDrWpsbOKjyKT0L82b9pjRF25Iy/xvDhFBfMohIqcVXEdm Q+sMCI9xw2M+WxYbStxy88JhD3W0jxPuXkIlmRCF9XWWnBJeVFiIGrI+xF0821Jh W0XoNB+ZW8mhQqVsAkqeUbkIuqVDy4IdIQt46ZWu90F04w+jceiHYzyDkFoZl5xF ZSW/f/D40FdJEjhX3daZ8RfuSUNOAedKMvvc3ie4Cfs+vyUT0Hq+3/aaqPi9CY3a TWdMYZWOjxUHYTeWQdAknAwDAFX7Nfl1/E0NN1ZqwMkmn0zFNa3Iy6/JggrTfFha okzv8YIJz9QndDKLq45VVRWaqcjA6vLytkp8sn0WCRH3Gdw+mwtWKLOqwEFTGwda RXTGSG0M/UrbuhU6fCcT0n1NmJMQV0Z93qaLTXDtttrTV8pkkrNlpScoFYsFXUXi Pcg2Bca2ePTxnKJFif+Sx6AN23u5x8q/o0mF9mSdR+PJNHONoDzXPbxkn34UX7gn 8WXy8iOP4ouSxOncjEhHUE4MdIN+YNTT+2mLJQf37jl87O4OPrgxqJpgiErHkZSJ kDvUNzuDOpegbeWiUmQrwsjWvMFq3qCld3fEvIXgvOOOzRvk5jHKxcu+YHlo69px 5Y0Tzx09gTbahbA90HIaGAZg5CTr5wxCw1P6Xgee/ZGKDdqHYCSM3PqfAUOncGr/ QBmDOaWMbqh6VkaDGROvHDUmlaWtTz+D+PPpYJTvq0wHqW8Xz0OQ6eEiny9E+Jgl xIo2vAl700w5enrbSh1exjPOaTjRBaoH9wBBVaXsalNyp9+XwTGd468ku0yutWJh JcZFjoPI+pGnM2QyesxHTlf6UUI57fJNgwcit4oVunpCOOrNg7LS8E29071jqPNl NMBLWnZen8GzCsWrVlb09j5I3BiBoaAe5KDMWVAWVekSqk+pr9AtwuwsMveIlGif Y2BYJxPfEy3mbJiawsaYqPng9i/pO1j/ql7jEXFk4OTcZO8BD6HZyGju3VjrgCWn RfziXD86Ei1Dq2CAf0nJzcC0X8mDs2hlmkCl6d5KMf9G1DDCg73WxJao8ZKt2ciZ dCVMxS767ZLuM8LT6Aznr25BbcKnEYlvkJlyrIVij85jUwVZqjiG02L0Ymhtl59k dVO4R5NC7raTO12WPJ3LHx5N7ksctpLKoeGbo13M8PX33x6+rGJGn65nIvLOKQQl wmTSH7owt/XiXTXucTLTyD+agEAGdUCcoqTfSSizPPGmJkr8wb9i+1aHbERiDL5w D8g569B8R6ZlrqxQrVVSjU5nMmiUUWpl+AdACV4PnboJVVHiu5kg+US05tXJwdbj n85eHR36O8cH/vd7J6cHR4fEna7bT/2gC3YYXoPzFU4oTmXlULvVLREgZPTSTvMg YSRXY76fvHZ1b2Nxa5sVLYUWaAJ5hp/Mp1oZVjIDLAepyAGm0yK/PNRvLqNcd2of BnyYC++q//Zdaf+NH+v+P/xGxd67/If7eOD+v1a7s67v/2vi+9bq+tr65/v/PsXn YNwPb7ZAK2XTCYjt6vO//6mevDyVWWrPeldpEsfZswldLqi++lGaPUuT3jPVrXdV hWWeROEVRhAS+KLM3dZKq92ugj4eiOWeWE7o2eC6uLioHypruCki2sBYotXcane3 4I9l5K8KQ1leXjaVN03ldntrdX2r2eXKCNP+UB/tzTVvtdkSVIBwuADgw0dn3KJB n6UrAayjcb8u9pSiqi/4l3GWwv8LnvhFP6z0Ft41wMR4RHM8+pCEA24uHx7f/Ab3 wjBqkRIA88i2hAK09CAgLcYJjnVhqMLEQuZLESRJcCtDi+kj0KT6BJn+cpGTXHrA 90dKNkqm45XLT8ywJRjM4N32SrdrsS4+ChtvZKwSaJX2mjgCjciM3Nla3dxqbkpG JpDIf2XtnsbSm5teq9kxHE3PXcXQx7do/6BZaato4bNngPYVG5jbbl0qw1oqU+PB imQLWbWW7gH3Ko7f5zuvUvjx+yCJ4iluwVLcHrPD4F+wU/mUzuyu98FoVsAkix3b 846U7X1iDisiMJPBWmubDofBs7ARx4kugqu01sVhfCU5bH2r297qdDSHIUxkiJJ2 RQ5bu4fDWmstr7W2ZliMCzYUj6HrMLnEo1FtzELDsyTgF8kJqxgm2o9uphMjJ8Cv llXRtRbOI7PEktXa4hniLgSNjlIaXaRQQhnAvAN0CY4Lha/TbDrAw820xLCArpMd Z0m9wbgpGKMgGksYbPeCK49lvo/1XHZiSdkffnJ5Veh/NjNtuLy0ISykLU4ypSiq 9sNzyUitrU5ra9VipA2bj6xWT2UjD1Q+PQLdj//znb97cLL38uzo5Cc8clJ56X/7 9uD12cEhPR3/5O+fHP28B09z9PTyaHfvZO/06O3Jyz2aqaDH29s0lXdShsgDZUhS OmUHri1zrNde1fz6t/r28p1DlYM3x/6ro6P/3I9IGSP9W0JJ9T6TidqttsNF8Cw0 ygUmApliW20ghza2uquagxBYgYVK5dD6fQzUXfVa3Q1LDkEBNFbzeseUpyvJlmZr G3j3B4oWow+vPLHIEQXam7APd6tUxyWhwrqcirZE0okjxiRrbtMVbgRWIfTHV1YX bvTz0HClNOrpxIoFARcxwLIwHhSXb6KDjM2CIYk6Awp7/3VcaygUVXgT25+amwBH YRZQuBIk7xVVeA0cQgdJmg1qbCeVXnHJDFi5WwUVQHMyZRY8NwHNjNOCbdN0NqoI 6MqOhyOsWbQsJ+aseYwTUeNmdOte7qrEQYDrW9G7kDWKRTLoIS8gyS6xl+uQcg77 Mc985Q7/URx4T4asjoCZYetWc+7oXSaelRurmd6J7OjB6AxZWY9f3Jm+TR+57jE8 miOlchWsVg05fs1ZNLM7XO/Kud+iBKfcuzubpebMXpBDib/DIjPX2yPY4E6uAGcB 3PEaJYElhG0oKWMWiv8gjWTWjgmbAoxSH7cDyq5r+7gbG157s6PVX02u+1qQXFzh 9yRt8Vcbv8KbKMMEwxrpQnjs0eWK/KrnX+GWh3rIkqAXnge997IybsHo2vSgq/Mr qz4QJL+8i6LEFlTYA86e1rJlo99obngbrTVlZ2jPYl9eeU8Ku/z+erlhzoFuT3BG stodp21wMktl44KCX7Qv31ONTBdkS6iIqXqNO7J+pvpaLIHvD5L493AsofND3VxU aflOr/GqGjzhzZcKogKS+xxgJatdTHkj6g8HZ68ERYHf7B2e7e0Sn2x0W95Gd1Px yT9LOpsUH5uMdv1/kaal7NkB9ux2tBnMu4J4i8Z4OsF+K2jFVGQ6qtrGhCEP+pT6 btHGXDSp7nWmbG15ywvmJWKawYRGw0ktfecEr9ACEIWV3vx4g2HjMK0XtjcaqlFe vBHvbALvbK5rGfPxx6V3Wz7y6EpnsbvubaytmlmsjLSlOGPwMNSRuo5LDpR3V3Ek NEAYwPKLjHdh1JisLW6Ea2fdE/9zF1sc+Gk2vU30wNQU/KNIFcj9WNRK6bfR8TY2 Nq2gVXvdazVb2pS3dmilXb8kzLkg0rnW78GQFi1mQi1RnoyjUqwgWN4wN29spWSX T0pcBwVAewi/eWLMlqO2ZygZ0QaqjXPHQrvv8mwxMRbfLJPL7OvlbCYlsWxUpT2S M1OkT4OnC10Efpm8w3QYzKWh20PQOMErJaZJKi9/0c6LnUdRHIZn7oOb01a/kwvA WFD65G980PU3POhKhIO/+ZQrj1dNAvGDJrcioTuNv0liIJJcX3EvGXw5FHL3mped ZUAgeIqhpifHnZ2cqakSyZ4gjdjrKLFR9bsZdmrBMt0ypqkWcMd4O1ddl97J7wIh 1JsS18a6hg8f74pEmLNSsKiFTEF1Tfkcv5Y533cF7n8+i/tNonhhHuUdsxKeTCJ5 tfP9nn96tnNW1oV1eyFCM7mopcmKihGknYEiif6hI26KONx8fh6vAQkyeRHHvKzG ywLdlrlT/+B09+CkLt+sAGuPKJdcz7/8kSqVA84/7YE2ChQsQski3eij7mWQV7zQ +TbwzWCJeAwGpHXKqW4yAKs4aBTCeMdROhLX9AMEeBEy3peJF5aHwZAPLEXZigZz Eo7iKz7cTaKC0qMv42GfLgDEoIjDnPjbRcIHT9gneBqMhCt/g6SHt6WbyzqHAV6G qxxHW+jshsMZstOSNSUcfqcZghKpWQbyTy68H8fX4hrR5EvaoTf23eEdXcFDST1A 4+UxZVzC6oDxA+ooQpXAJFRlNlKJDJcsZv3cwj3S03jO6kjbvUspP1Yaaok+uLtf h1KiWGKZyI5a/EcVqccehdKoZvUWdXm+Y7e7Mo1pZYiWLfKZ2pIxyqsCzherG8xz t3vW0IGVDoXj/buTpC5416O+0+7HaYjJvbSeaKHXQV4F02GmI4sN4r9AeiIruAIJ GLTgjVq6TiGdxGP9ExLsZbGE8kha1K+iQLk1+ofHGgSj1HjbXPM2Wy3Lw7Z+dq0X pKEfv687nh/9Iw/Cbrv1iWJk7jseH2/GWI6n3U/RZawKUfQaQdMMy3jV+W0H9hzR 1sXbe9EgRqpJP3LigzmrQjDyRu8xX/ANBbIp3fqL/dAN/Vhe9GjJx9GWuEy/h/cj SkaGInX62aoG2mmM6unk4PTl0akF2NUqQkkWCog3W2hHr3YsH/4TTM7SzJlhTscV yc/4M5C4MuB/tWdiNkw+/eQ6gYKJFHP/0ow70lBHvby8FM2J27/CKWWLerO14W22 V629lWanhYkFZpdXjj4FDdf37eHLX4egcj65qXnYUXqKwMZxRHmr6W61U06mvDdK TXbxojQhZp0qz12VVjrmNgiy1ZY95i6OeUOnA3HQQqbBM7tpXNQOtYx06xlzLBzj GBUGL1W3aVfye00m+Gl5GFYLo87mpPPDZw51FWMrzjh7bx+Ale5BzR2OupU/EHhn K/08m65pDTXVDka5fVUZ60Eap9jgyTUL1xvR0lNXG8lx2IFHqZ8fdgatBRUpRO/V 6tK9y2v0I6nSNbUqzkx8o625LWGNiw8tyO4cv9OhFv9AH6UL2+atYZycN8JHVqTl IK7BwNY/tsGv7bXlkEt1O58T0HmXT1ksUjLm7GW1TIkEzi8COD8YoRlR8MbQ2aU8 mI5xJWSodHouQ6l4Te112MetlqiPPFfj6GyN2tKBVhmJxV8/BQuFfjsNY7kMgPbr ZB3+VbTShb8Owm7D3khutTe9VqvbtYJnvA5mLAHjVdEVPpyvkmN9IYmkzVjfxIVy 69zZBsnbrarZrMXuRJaMR/DI5V4r7HjOXvCl673UxfgrP0W3ZJjJbuoM+/5fo3v6 wHOdmaHj0Z/7RoizWybR5KLHt6whhf4BD601HhBzwolt9eLJrRPGtdIWimLOlnBs GaBhAOv47enB4Xf+28MDTGWxguNvx1HPnFW/MlFw2a+psTemE36mYOdUwYN25Rq2 1Wx2Oa5rLbX1Niw1azuyYu9uYrdyMZnoEorFK4GPAeaV4d6BTC7TK6ySiw5LEVim eVFCOQxvyqmNOjdf/EXEh5SQlrWWz8xWw/2u6tV/pZrga+zUYCROeF90Ic4kEblz mHYU9MAAj9NLqTboC3jqdBKG/elkS4QBuMZaRuMvF9DF2ZQ5iseUwbUtZ8pOp+O1 Ot2m8mDx5qsHd/PECf74wt7hyz3x8ujt4VneKbViJnOiuCfKbKCMfsdLQTeDtn3M Zr910GtKt765pdsyp6294bXWjLP3f2QctlOV86geMbLSGeu2MfOrbWvs9SZ4uutt Y6pbEeCRlCAy2Mx2Ojq/2uncshJAcgvuLxjMtuDB2GU8zSTnP2QUj2ZYn9wasyac 33vJRZSsaLrSSTJAtGV5Ru4laDb+QqiN9NquCXdewv9ZrNamvGakTrYaHXv+qt+o zRD3aFi1Njes7e3CpJBHJU+AzznqTLvZM39VXZjQuutr86uCS03JL6hv4J+Nexjl fpysiOQ/iVw5ATe7mNHYMgSUwcBKydZ5yd65/jUya+PcxtPKCLC1rrFaZm2lP3X7 StJ+YxNov2mZ1R9hOPCP1jsff1xl0wYz1oFpazWthICy/ShhSQK9GcVUmZPCwJa4 5gT2rBwBuRPPO/C5LXhzT4BrEfDEbG547Xaz6xphHxNhT+hJmnMnyZm8vzCg8hnZ aMKMbLoz4mxgPiUl5QnLqpT78sxXdBb/YnpIWe6FPn8vBWB7HWRKe8Od649LCjtZ 5OMTpJia8gBZSjkGSARkWjW6S904ZJPEDnjYG0fYnfQGNfpF2tmEM/cZzWCkh0lU zMRRy7sD9hL8o1P5/r2x3Jel9cgRlc5WdxMk7lrTWt+5n6hwZJcVkJlzNtyKrGXG otfa3SwDmWi9jjpu3dZxHwUTi5Az8Sml1Hp7DfBbdc68tlse/GOy9fh2DTwbVcdc cH0aBqxN/Tef4heURYZZJdMk3C5pq8/O1PAGBvn349vaJ20Ygl1SCmepAEdZ+dYu 0wwMyGuSz2Qz69ODr6PzZyk4lZ/+JLXV88zD1J015yx1Z00oXHHOLQiVTXNCsNnd am7oM1oVgoL84FZ/yokc0CwqjCoE/u5P6kdjH3xytaPxB/KqTJHABBTlrm9V1eHi /WhIP6I45ZSzlJNCOPsivAGaoLTPVGIFp7Bwogv/TDAsIwaURvijflG2QL9CNQl7 Ed0GmYYr4gcMLKexGIdhH10b+XaZXEIE+CboeRoQxkf0D19RoFlFpWMzAGyWhCuy EfAfvRgGGQx2hNJrYRT0Frbke1kHiYAb7nTXtLx5KUqhtA7/N+zKvHXP4S9ZjP7J I+ARzdL7AfIjVPHwH6IC/7gxQqCmVd0HwlaVgPyFSVYziZ/X6ogHgUDWQgbRGan/ dxlkX/6m88zpXtFQ8DKfHyJ3my9lqP3pOR1exmuervgmcRtzClGlGlB+GCX5WVvq B9souNWLp8M+R6ffnrzWcJB44U2Av8ZnseMTWOMTs8W/fU3G58/nz+fP58/nz+fP 58/nz3/N538B2FEpBwCgAAA= --11364638-0-3248466697=:9007--
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4