PK!2|==pipm/__init__.py#! /usr/bin/env python # -*- coding: utf-8 -*- from __future__ import absolute_import from pip import _internal as p # patch for program name from pipm.commands import InstallCommandPlus, UninstallCommandPlus, UpdateCommand, FreezeCommandPlus def get_prog(): return 'pipm' p.get_prog = get_prog p.commands_dict[InstallCommandPlus.name] = InstallCommandPlus p.commands_dict['i'] = InstallCommandPlus p.commands_dict[UninstallCommandPlus.name] = UninstallCommandPlus p.commands_dict['rm'] = UninstallCommandPlus p.commands_dict[FreezeCommandPlus.name] = FreezeCommandPlus p.commands_dict['save'] = FreezeCommandPlus p.commands_dict['update'] = UpdateCommand p.commands_dict['upgrade'] = UpdateCommand p.commands_dict['u'] = UpdateCommand # endpatch def main(): p.main() if __name__ == '__main__': main() PK!&}}pipm/__main__.py#! /usr/bin/env python # -*- coding: utf-8 -*- import sys import pipm if __name__ == '__main__': sys.exit(pipm.main()) PK!/qwՇpipm/commands.pyfrom __future__ import absolute_import from pip._internal.commands import UninstallCommand, FreezeCommand from pip._internal.commands import install from pipm.operations import get_orphaned_packages from . import file INTERACTIVE_UPDATE = "--interactive-update" def store_req_environment(option, opt_str, value, parser, *args, **kwargs): parser.values.req_environment = opt_str.strip('-') orig_install_given_reqs = install.install_given_reqs def patched_install_given_reqs(to_install, install_options, global_options=(), *args, **kwargs): from pip._internal.utils.logging import indent_log accepted_reqs = [] with indent_log(): confirm_update = INTERACTIVE_UPDATE in install_options if install_options and confirm_update: install_options.remove(INTERACTIVE_UPDATE) for requirement in to_install: if confirm_update: want_to_install = input('Do you want to update {}? [Y/n]'.format(requirement.req)) if str(want_to_install).lower() in {'no', 'n', }: continue accepted_reqs.append(requirement) return orig_install_given_reqs(accepted_reqs, install_options, global_options, *args, **kwargs) # patch the original function install.install_given_reqs = patched_install_given_reqs class InstallCommandPlus(install.InstallCommand): def __init__(self, *args, **kw): """ Args: *args: **kw: """ super(InstallCommandPlus, self).__init__(*args, **kw) cmd_opts = self.cmd_opts cmd_opts.add_option( '--dev', dest='req_environment', action='callback', callback=store_req_environment, help="Save package requirements to `dev-requirements.txt` or `requirements/dev.txt` or" " `requirements/development.txt`" ) cmd_opts.add_option( '--all', dest='req_environment', action='callback', callback=store_req_environment, help="install all requirements from all environments. (E.g. `pipm install --all` will install " "requirements from all requirements-*.txt files.)" ) cmd_opts.add_option( '--test', dest='req_environment', action='callback', callback=store_req_environment, help="Save package requirements to `test-requirements.txt` or `requirements/test.txt` or" " `requirements/test.txt`" ) cmd_opts.add_option( '--prod', dest='req_environment', action='callback', callback=store_req_environment, help="Save package requirements to `prod-requirements.txt` or `requirements/production.txt` or" " `requirements/prod.txt`" ) cmd_opts.add_option( '--env', dest='req_environment', action='store', help="Save package requirements to `prod-requirements.txt` or `requirements/production.txt` or" " `requirements/prod.txt`" ) def parse_args(self, args): """ when no argument given it fills with `-r requirements.txt` as default Args: args (list): Returns: options, list: """ options, args = super(InstallCommandPlus, self).parse_args(args) if not options.requirements and ((len(args) == 1 and set(args) == {'--all'}) or not args): env = options.req_environment upgrade = options.upgrade if env == 'all': req_args = [] for req in file.get_req_filenames(): req_args.append('-r') req_args.append(req) else: req_args = ['-r', file.get_req_filename(env)] options, args = super(InstallCommandPlus, self).parse_args(req_args) options.req_environment = env options.upgrade = upgrade options.no_save = True return options, args def run(self, options, args): """ wrapper around pip.commands.InstallCommand Args: options: args: Returns: pip.req.RequirementSet: """ result = super(InstallCommandPlus, self).run(options, args, ) if not hasattr(options, 'no_save'): # save changes to file if any file.save(env=options.req_environment, user_reqs=result) return result class FreezeCommandPlus(FreezeCommand): """ A wrapper around standard freeze command that updates currently installed packages to requirement files after showing the packages list in standard output. """ def run(self, options, args): res = super(FreezeCommandPlus, self).run(options, args) file.save() return res class UninstallCommandPlus(UninstallCommand): """ a drop-in replacement of pip's uninstall command. It removes the entry from requirements file after a package is removed. """ def run(self, options, args): removable_pkgs = get_orphaned_packages(args) if removable_pkgs: print('Following packages are no longer required by any of the installed packages: \n', '\n'.join( removable_pkgs)) res = super(UninstallCommandPlus, self).run(options, (args + list(removable_pkgs))) file.save() return res class UpdateCommand(InstallCommandPlus): name = 'update' usage = """ %prog [environment-to-update] %prog [package-names-to-update]""" summary = 'Update packages (equivalent to that of `install` with --upgrade)' def __init__(self, *args, **kw): """ Args: *args: **kw: """ super(UpdateCommand, self).__init__(*args, **kw) cmd_opts = self.cmd_opts cmd_opts.add_option( '--auto-update', dest='interactive_update', default=True, action='store_false', help="Update packages without user input", ) def parse_args(self, args): """ when no argument given it fills with `-r requirements.txt` as default Args: args (list): Returns: options, list: """ options, args = super(UpdateCommand, self).parse_args(args + ['--upgrade']) opts = options.install_options or [] if options.interactive_update: opts.append(INTERACTIVE_UPDATE) options.install_options = opts return options, args PK!"& ?? pipm/file.pyfrom __future__ import absolute_import, unicode_literals import errno from collections import OrderedDict import logging import os from pip._internal.download import PipSession, get_file_content from pip._internal.req import req_file, RequirementSet from pip._internal.req.req_install import InstallRequirement from . import operations, setup_cfg logger = logging.getLogger(__name__) def _new_line(filename): """ append `\n` to the end of the file if it doesn't exist Args: filename: """ with open(filename, 'ab+') as f: # check for empty file f.seek(0) if f.read(1) == b'' and f.read() == b'': return try: f.seek(-1, os.SEEK_END) if f.read(1) != b'\n': f.write(b'\n') except OSError: pass def get_env_reqfile(*paths, **kw): """ from the list of paths return the one that exists. If it doesn't exists then create with appropriate starter line Args: env: base_file_name: *paths: Returns: str: """ base_file_name = kw.get('base_file_name', '') requirements_dir = os.path.join('requirements', '') for path in paths: if os.path.exists(path): if path == 'requirements.txt': base_path = os.path.join('requirements', 'base.txt') if os.path.exists(base_path): return base_path return path # create file if it doesnt exist filename = paths[0] # prefer the first pattern in the list # if requirements directory exists then prefer creating files inside that one if os.path.exists(os.path.join(os.curdir, 'requirements')): for path in paths: if requirements_dir in path: filename = path if os.path.dirname(filename) and not os.path.exists(os.path.dirname(filename)): try: os.makedirs(os.path.dirname(filename)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise if not os.path.exists(filename): with open(filename, 'wb') as f: if base_file_name: if filename != base_file_name: f.write( '-r {}'.format( 'base.txt' if requirements_dir in filename else base_file_name ).encode('utf-8') ) return filename def get_patterns(*envs): """ Args: *envs: Returns: list: """ patterns = [] for env in envs: patterns.append('{}requirements.txt'.format('' if env == 'base' else env + '-')) patterns.append(os.path.join('requirements', '{}.txt'.format(env))) if env != 'base': patterns.append('requirements-{}.txt'.format(env)) return patterns def get_req_filename(env=''): """ Args: dev: test: prod: env: Returns: str: """ BASE_PTRN = ('base',) DEV_PTRN = ('dev', 'development',) TEST_PTRN = ('test',) PROD_PTRN = ('prod', 'production',) envs = DEV_PTRN if env == 'dev' else PROD_PTRN if env == 'prod' else TEST_PTRN if env == 'test' else ( env,) if env else BASE_PTRN paths = get_patterns(*envs) fname = get_env_reqfile(*paths, base_file_name='' if not env else get_env_reqfile(*get_patterns(*BASE_PTRN))) _new_line(fname) return fname def get_req_filenames(): """return all requirement files in the current project that matches the standard requirements filename pattern""" filenames = set() # if requirements directory exists then add those req_dir = os.path.join(os.curdir, 'requirements') if os.path.exists(req_dir): for fn in os.listdir(req_dir): filename = os.path.join('requirements', fn) if os.path.isfile(filename): if filename.endswith('.txt'): filenames.add(filename) else: # walk current directory for filename in os.listdir(os.curdir): if os.path.isfile(filename): if filename.endswith('requirements.txt'): filenames.add(filename) elif filename.startswith('requirements-'): filenames.add(filename) return filenames def parse_comes_from(comes_from, env): """ parse comesfrom if valid otherwise return the filename for the environment Args: comes_from ([str]): env (str): Returns: str, int: filename and line number """ if comes_from: comes_from = comes_from.split() return comes_from[1], int(comes_from[3].strip(')')) filename = get_req_filename(env) return filename, None def _uniq_resources(reqs): """ Args: reqs (list[InstallRequirement]): Returns: OrderedDict: """ uniq_reqs = OrderedDict() for req in reqs: if req.name in uniq_reqs: old_req = uniq_reqs[req.name] if not req.comes_from and old_req.comes_from: req.comes_from = old_req.comes_from uniq_reqs[req.name] = req return uniq_reqs def _cluster_to_file_reqs(reqs, env): """ Args: reqs (list): Returns: OrderedDict: """ filereqs = OrderedDict() for req in reqs: # type: InstallRequirement filename, line_num = parse_comes_from(req.comes_from, env) if filename not in filereqs: filereqs[filename] = [] req.filename = filename req.line_num = line_num filereqs[filename].append(req) return filereqs def save(env='', session=None, user_reqs=None): """ save installed requirements which is missing in the requirements files Args: env (str): session: user_reqs (RequirementSet): list of strings that are explicitly given as argument to the user installing """ session = session or PipSession() reqs = [] # create base file if it doesnt exists env_filename = get_req_filename(env) for file in get_req_filenames(): reqs += list(req_file.parse_requirements(file, session=session)) uniq_reqs = _uniq_resources(reqs) file_reqs = _cluster_to_file_reqs(list(uniq_reqs.values()), env) for filename in get_req_filenames(): if not file_reqs.get(filename): file_reqs[filename] = [] if user_reqs: setup_cfg.write_to_setup_cfg(user_reqs, env) write_to_req_files(file_reqs, session, env_filename, uniq_reqs) def write_to_req_files(file_reqs, session, env_filename, uniq_reqs): # first step process the requirements and split them into separate for each of the file installations = operations.get_frozen_reqs() for filename in file_reqs: # type: str _, content = get_file_content(filename, session=session) orig_lines = enumerate(content.splitlines(), start=1) joined_lines = req_file.join_lines(orig_lines) lines = OrderedDict(joined_lines) # 1. save new requirements if filename == env_filename: installed = set(installations.keys()).difference(set(uniq_reqs.keys())) for new_req in installed: line_num = len(lines) + 1 lines[line_num] = str(installations[new_req]).strip() for req in file_reqs[filename]: frozenrequirement = installations.get(req.name) if frozenrequirement: # 2. updates lines[req.line_num] = str(frozenrequirement).strip() else: # 3. removals lines.pop(req.line_num) # 4. finally write to file with open(filename, 'wb') as f: for line in lines: cnt = lines[line].strip() if cnt: f.write((cnt + '\n').encode('utf-8')) PK!a a pipm/operations.pyfrom __future__ import absolute_import import logging from pip._internal.operations.freeze import FrozenRequirement from pip._internal.compat import stdlib_pkgs from pip._internal.commands.freeze import DEV_PKGS from pip._internal.utils import misc from pip._internal.utils.misc import get_installed_distributions import pkg_resources from six.moves import reload_module DEV_PKGS = DEV_PKGS.union({'pipm', }) logger = logging.getLogger(__name__) STD_PKGS = stdlib_pkgs.union(DEV_PKGS) def get_dependency_links(): dep_links = [] reload_module(pkg_resources) for dist in pkg_resources.working_set: if dist.has_metadata('dependency_links.txt'): dep_links.extend( dist.get_metadata_lines('dependency_links.txt') ) return dep_links def get_frozen_reqs(): dependency_links = get_dependency_links() installations = {} for _, dist in get_distributions().items(): try: req = FrozenRequirement.from_dist( dist, dependency_links ) except pkg_resources.RequirementParseError: logger.warning("Could not parse requirement: %s", dist.project_name) continue installations[req.name] = req return installations def get_distributions(): """ Returns: dict: """ reload_module(misc.pkg_resources) reload_module(misc) return {dist.project_name.lower(): dist for dist in get_installed_distributions(local_only=None, skip=STD_PKGS, user_only=None)} def get_orphaned_packages(pkgs): """ return list of packages that is only required by the pkgs given but not other installed packages Args: pkgs (list): Returns: list: """ dists = get_distributions() removed_packages = [] for pkg in pkgs: # type: str pkgl = pkg.lower() if pkgl in dists: removed_packages.append(dists.pop(pkgl)) orphaned_pkgs = set() for dist in removed_packages: # type: pkg_resources.DistInfoDistribution for r in dist.requires(): orphaned_pkgs.add(r.name) all_requires = set() for dist in dists: for r in dists[dist].requires(): all_requires.add(r.name) orphaned_pkgs = orphaned_pkgs.difference(all_requires) return list(orphaned_pkgs.difference(set(STD_PKGS))) PK!)dU pipm/setup_cfg.pyimport codecs from pip._internal.req import InstallRequirement from setuptools.config import read_configuration from six.moves import configparser import os SETUP_FILE_NAME = 'setup.cfg' def _get_existing_reqs(config_dict, base_key, key, reqs): reqs_to_write = [] if config_dict: # parsed config existing = [] if base_key in config_dict and key in config_dict[base_key]: existing = config_dict[base_key][key] elif '.' in base_key and base_key not in config_dict: options, extras_require = base_key.split('.', 1) if (options in config_dict and extras_require in config_dict[options] and key in config_dict[options][extras_require]): existing = config_dict[options][extras_require][key] for _req in existing: # type: str if not any((i in _req for i in reqs)): reqs_to_write.append(_req) return reqs_to_write def _set_config_key(config, config_dict, base_key, key, reqs): if base_key not in config: config.add_section(base_key) reqs_to_write = _get_existing_reqs(config_dict, base_key, key, reqs) reqs_to_write.extend(reqs.values()) reqs_string = "".join(map(lambda x: "\n" + x, reqs_to_write)) config[base_key][key] = reqs_string def _write_to_setup_cfg(config, config_dict, reqs, env=None): """ Args: config: reqs (dict): env (str): """ if env: base_key = 'options.extras_require' key = 'testing' if 'test' in env else str(env) else: base_key = 'options' key = 'install_requires' _set_config_key(config, config_dict, base_key, key, reqs) def write_to_setup_cfg(user_reqs, env=None): """ create/update setup.cfg file Args: user_reqs (RequirementSet): list of user requirements file_obj: file object to write to """ config = configparser.ConfigParser() config_dict = None if os.path.exists(SETUP_FILE_NAME): config.read(SETUP_FILE_NAME) config_dict = read_configuration(SETUP_FILE_NAME) with codecs.open(SETUP_FILE_NAME, 'w') as file_obj: reqs = {} for req in user_reqs.requirements.values(): # type: InstallRequirement if not req.comes_from: req_string = str(req.req) if not any((i in req_string for i in {'<', '>', '=', '~'})): req_string = req.req.name + '~=' + str(req.installed_version) reqs[req.req.name] = req_string if reqs: _write_to_setup_cfg(config, config_dict, reqs, env) config.write(file_obj) PK!HS32!"&pipm-10.4.3.dist-info/entry_points.txtN+I/N.,()*,ȵVy\\PK!`[HHpipm-10.4.3.dist-info/LICENSEMIT License Copyright (c) 2017 Noorhteen Raja J Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HMWXpipm-10.4.3.dist-info/WHEEL A н#Z@Z|Jl~6蓅 Λ MU4[PYBpYD*Mͯ#ڪ/̚?ݭ'%nPK!H;{zvpipm-10.4.3.dist-info/METADATAXmsܶ_fZK#,9i&7'jj!)q$Ixϳ Mx[b|,:m.wVKNOogǮ=,l[ekJ, t!;oҮn%UXJ%oLVnp-nASc[.TM:-R(ӍK|uΝ]lwNVٝOH:5v)TRWӅ7LKT{ˋwOϟeOp?\fgɫJ:Zj*Ӓ⣗sbߊT| XXuUSR\uVMH,*f{#Xkqն dXY׺و_dHOݟ}Hgݟ}ho-GK}cZ]G{i,eclF,s'/_<=d?\fZ'}e(Z|zsh2^24&INBV'Vt -~QHuestOl??N`gi덐Nh#d"y#X)Rbw~4kѲ duHbI'I*´$-LS]*14ډJ"99u.dRՋ55:I,_B@t-[Z-Vi"v]O^9T}Y> ^e(3xe .+'?*H 2uc GdB''j1B]Dž2ޝOf炽LC3yl* EZ&wIpk{I䫸2B΀i`mCyJSEˀJ ]󇷈Ã^yE*=pL$Y P;,6!̇j}HR^0U;V?^*\ o-O L6Hؿ!;źk@/[5=AB*kRBŢeN4 ]3y-t(w ~i4Vҩg=𱽅`2j㇃yO4_cjD13p6@F{x'O>z44k<V:UY>`4 8hg71bԬ YK< H< @؃ bhA˙\;_cޘ ! ~ _Ģlv*4DBlvk6ML)L0c0uutS[P-}AI=˵?nGoD O#8?bS 2-EkՆr\s fBn棁bR 1y%+gN(t'%yC VՁf3L,l (w\D]߃dWE8K1c(Wrb'(*#h5x4S%YTJ]w$\Q*L l׎dD(1kLL]1Hp}LrU"㐝mq_J:e&P+jꨐxwC! Ύq-n1#ܞ&4o+GuRBn\]Ie,d80FcL &IQi"RƭS*E6s;1Wd)@!*S#Ўݟ w"n 5ם*3+utj^iI*qyS5cKIWB SO~pjz,nﳳ|hM|} \Wlٮ^R"m Bv7O9<\''. /3c7Ӟy$RVerw P&O/T2)BWP 4H_8L[U1l#. WFFpi\8DԡE7[L=I~PK!H.pipm-10.4.3.dist-info/RECORDuIsP}~ ǰ^ P S!Lڤ^=νuZ^_7U;S]p@uZIi윘|P0%eVŠ_ei5o7}yxnPDŽ_yjouz(qc +T OpԐG?fIV4pdy$g{>X0U "qgbE|B 'Zܣy}vIL7Eݾv94eQ;Jt:<s o:9p fF{dGh,M.!$ҶNQ/PK!2|==pipm/__init__.pyPK!&}}kpipm/__main__.pyPK!/qwՇpipm/commands.pyPK!"& ?? pipm/file.pyPK!a a 4>pipm/operations.pyPK!)dU Gpipm/setup_cfg.pyPK!HS32!"&vRpipm-10.4.3.dist-info/entry_points.txtPK!`[HHRpipm-10.4.3.dist-info/LICENSEPK!HMWX^Wpipm-10.4.3.dist-info/WHEELPK!H;{zvWpipm-10.4.3.dist-info/METADATAPK!H.`pipm-10.4.3.dist-info/RECORDPK ,c