├── README.md ├── autoinstaller.py ├── ez_setup.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | # autoinstaller 2 | 3 | (Warning: apparently David Beazley invented this before me https://www.youtube.com/watch?v=0oTh1CXRaQ0&feature=youtu.be&t=2h39m46s although he thinks it is a bad idea, so use it in moderation!) 4 | 5 | A package that automagically installs from PyPi everything you need as soon as you try import it. 6 | For example, let's assume that you do not have the tornado web server. 7 | 8 | >>> import tornado 9 | Traceback (most recent call last): 10 | File "", line 1, in 11 | ImportError: No module named tornado 12 | 13 | But if you have autoinstaller 14 | 15 | >>> import autoinstaller 16 | >>> import tornado 17 | ... 18 | Processing dependencies for tornado 19 | ... 20 | Installed ... 21 | Finished processing dependencies for tornado 22 | >>> 23 | 24 | And from now on the package can be used without existing or restarting the process. 25 | 26 | Of course you must install autoinstaller first the old fashion way: 27 | 28 | >>> import autoinstaller 29 | 30 | ## License 31 | 32 | BSD version 3 - created by Massimo Di Pierro (http://experts4solutions.com) 33 | -------------------------------------------------------------------------------- /autoinstaller.py: -------------------------------------------------------------------------------- 1 | # Created by Massimo Di Pierro (http://experts4solutions.com) @2016 BSDv3 License 2 | 3 | import sys, imp 4 | from ez_setup import use_setuptools 5 | use_setuptools() 6 | from setuptools.command.easy_install import main 7 | 8 | class SmartImporter(object): 9 | tried_modules=set() 10 | domain_modules = set() 11 | 12 | def find_module(self, fullname, path=None): 13 | if fullname in self.domain_modules: 14 | return self 15 | if fullname in self.tried_modules: 16 | return None 17 | else: 18 | self.tried_modules.add(fullname) 19 | try: 20 | main([fullname]) 21 | return self 22 | except: 23 | return None 24 | 25 | def load_module(self, fullname): 26 | if fullname in sys.modules: 27 | return sys.modules[fullname] 28 | mod = imp.new_module(fullname) 29 | mod.__loader__ = self 30 | sys.modules[fullname] = mod 31 | if fullname not in self.domain_modules: 32 | self.domain_modules.add(fullname) 33 | return mod 34 | 35 | sys.meta_path = [SmartImporter()] 36 | 37 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap setuptools installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import sys 17 | DEFAULT_VERSION = "0.6c11" 18 | DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] 19 | 20 | md5_data = { 21 | 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 22 | 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 23 | 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 24 | 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 25 | 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 26 | 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 27 | 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 28 | 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 29 | 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 30 | 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 31 | 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090', 32 | 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4', 33 | 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7', 34 | 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5', 35 | 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de', 36 | 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b', 37 | 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2', 38 | 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086', 39 | 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 40 | 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 41 | 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 42 | 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 43 | 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 44 | 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 45 | 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 46 | 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 47 | 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 48 | 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 49 | 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 50 | 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 51 | 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 52 | 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 53 | 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 54 | 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 55 | 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 56 | 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 57 | 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 58 | 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', 59 | 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', 60 | 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', 61 | 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', 62 | 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', 63 | } 64 | 65 | import sys, os 66 | try: from hashlib import md5 67 | except ImportError: from md5 import md5 68 | 69 | def _validate_md5(egg_name, data): 70 | if egg_name in md5_data: 71 | digest = md5(data).hexdigest() 72 | if digest != md5_data[egg_name]: 73 | print >>sys.stderr, ( 74 | "md5 validation of %s failed! (Possible download problem?)" 75 | % egg_name 76 | ) 77 | sys.exit(2) 78 | return data 79 | 80 | def use_setuptools( 81 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 82 | download_delay=15 83 | ): 84 | """Automatically find/download setuptools and make it available on sys.path 85 | 86 | `version` should be a valid setuptools version number that is available 87 | as an egg for download under the `download_base` URL (which should end with 88 | a '/'). `to_dir` is the directory where setuptools will be downloaded, if 89 | it is not already available. If `download_delay` is specified, it should 90 | be the number of seconds that will be paused before initiating a download, 91 | should one be required. If an older version of setuptools is installed, 92 | this routine will print a message to ``sys.stderr`` and raise SystemExit in 93 | an attempt to abort the calling script. 94 | """ 95 | was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules 96 | def do_download(): 97 | egg = download_setuptools(version, download_base, to_dir, download_delay) 98 | sys.path.insert(0, egg) 99 | import setuptools; setuptools.bootstrap_install_from = egg 100 | try: 101 | import pkg_resources 102 | except ImportError: 103 | return do_download() 104 | try: 105 | pkg_resources.require("setuptools>="+version); return 106 | except pkg_resources.VersionConflict, e: 107 | if was_imported: 108 | print >>sys.stderr, ( 109 | "The required version of setuptools (>=%s) is not available, and\n" 110 | "can't be installed while this script is running. Please install\n" 111 | " a more recent version first, using 'easy_install -U setuptools'." 112 | "\n\n(Currently using %r)" 113 | ) % (version, e.args[0]) 114 | sys.exit(2) 115 | else: 116 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 117 | return do_download() 118 | except pkg_resources.DistributionNotFound: 119 | return do_download() 120 | 121 | def download_setuptools( 122 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 123 | delay = 15 124 | ): 125 | """Download setuptools from a specified location and return its filename 126 | 127 | `version` should be a valid setuptools version number that is available 128 | as an egg for download under the `download_base` URL (which should end 129 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 130 | `delay` is the number of seconds to pause before an actual download attempt. 131 | """ 132 | import urllib2, shutil 133 | egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) 134 | url = download_base + egg_name 135 | saveto = os.path.join(to_dir, egg_name) 136 | src = dst = None 137 | if not os.path.exists(saveto): # Avoid repeated downloads 138 | try: 139 | from distutils import log 140 | if delay: 141 | log.warn(""" 142 | --------------------------------------------------------------------------- 143 | This script requires setuptools version %s to run (even to display 144 | help). I will attempt to download it for you (from 145 | %s), but 146 | you may need to enable firewall access for this script first. 147 | I will start the download in %d seconds. 148 | 149 | (Note: if this machine does not have network access, please obtain the file 150 | 151 | %s 152 | 153 | and place it in this directory before rerunning this script.) 154 | ---------------------------------------------------------------------------""", 155 | version, download_base, delay, url 156 | ); from time import sleep; sleep(delay) 157 | log.warn("Downloading %s", url) 158 | src = urllib2.urlopen(url) 159 | # Read/write all in one block, so we don't create a corrupt file 160 | # if the download is interrupted. 161 | data = _validate_md5(egg_name, src.read()) 162 | dst = open(saveto,"wb"); dst.write(data) 163 | finally: 164 | if src: src.close() 165 | if dst: dst.close() 166 | return os.path.realpath(saveto) 167 | 168 | def main(argv, version=DEFAULT_VERSION): 169 | """Install or upgrade setuptools and EasyInstall""" 170 | try: 171 | import setuptools 172 | except ImportError: 173 | egg = None 174 | try: 175 | egg = download_setuptools(version, delay=0) 176 | sys.path.insert(0,egg) 177 | from setuptools.command.easy_install import main 178 | return main(list(argv)+[egg]) # we're done here 179 | finally: 180 | if egg and os.path.exists(egg): 181 | os.unlink(egg) 182 | else: 183 | if setuptools.__version__ == '0.0.1': 184 | print >>sys.stderr, ( 185 | "You have an obsolete version of setuptools installed. Please\n" 186 | "remove it from your system entirely before rerunning this script." 187 | ) 188 | sys.exit(2) 189 | 190 | req = "setuptools>="+version 191 | import pkg_resources 192 | try: 193 | pkg_resources.require(req) 194 | except pkg_resources.VersionConflict: 195 | try: 196 | from setuptools.command.easy_install import main 197 | except ImportError: 198 | from easy_install import main 199 | main(list(argv)+[download_setuptools(delay=0)]) 200 | sys.exit(0) # try to force an exit 201 | else: 202 | if argv: 203 | from setuptools.command.easy_install import main 204 | main(argv) 205 | else: 206 | print "Setuptools version",version,"or greater has been installed." 207 | print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' 208 | 209 | def update_md5(filenames): 210 | """Update our built-in md5 registry""" 211 | 212 | import re 213 | 214 | for name in filenames: 215 | base = os.path.basename(name) 216 | f = open(name,'rb') 217 | md5_data[base] = md5(f.read()).hexdigest() 218 | f.close() 219 | 220 | data = [" %r: %r,\n" % it for it in md5_data.items()] 221 | data.sort() 222 | repl = "".join(data) 223 | 224 | import inspect 225 | srcfile = inspect.getsourcefile(sys.modules[__name__]) 226 | f = open(srcfile, 'rb'); src = f.read(); f.close() 227 | 228 | match = re.search("\nmd5_data = {\n([^}]+)}", src) 229 | if not match: 230 | print >>sys.stderr, "Internal error!" 231 | sys.exit(2) 232 | 233 | src = src[:match.start(1)] + repl + src[match.end(1):] 234 | f = open(srcfile,'w') 235 | f.write(src) 236 | f.close() 237 | 238 | 239 | if __name__=='__main__': 240 | if len(sys.argv)>2 and sys.argv[1]=='--md5update': 241 | update_md5(sys.argv[2:]) 242 | else: 243 | main(sys.argv[1:]) 244 | 245 | 246 | 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup 4 | 5 | setup(name='autoinstaller', 6 | version='0.1', 7 | description='A library that automatically imports from Pypi everything you try import', 8 | author='Massimo DiPierro', 9 | author_email='massimo.dipierro@gmail.com', 10 | long_description=open("README.md").read(), 11 | url='https://github.com/mdipierro/autoinstaller', 12 | install_requires=[], 13 | py_modules=["autoinstaller"], 14 | license= 'BSD', 15 | keywords='package management', 16 | ) 17 | --------------------------------------------------------------------------------