Remove vendored pipreqs

Also remove dependencies:

     yarg
     docopt
This commit is contained in:
Matt Davis
2022-03-21 05:56:21 -04:00
committed by Oz N Tiram
parent 1207b34e14
commit 21ccd11bdf
19 changed files with 1 additions and 3774 deletions
-1
View File
@@ -15,7 +15,6 @@ recursive-include pipenv *.rst
include pipenv/patched/notpip/_vendor/vendor.txt
include pipenv/patched/safety.zip pipenv/patched/patched.txt
include pipenv/vendor/pipreqs/stdlib pipenv/vendor/pipreqs/mapping
include pipenv/vendor/*.txt pipenv/vendor/pexpect/bashrc.sh
include pipenv/vendor/Makefile
include pipenv/pipenv.1
+1 -1
View File
@@ -809,7 +809,7 @@ class BaseCommand:
parsing methods that do not depend on the Click parser.
For instance, this can be used to bridge Click and other systems like
argparse or docopt.
argparse.
Because base commands do not implement a lot of the API that other
parts of Click take for granted, they are not supported for all
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2012 Vladimir Keleshev, <vladimir@keleshev.com>
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.
-579
View File
@@ -1,579 +0,0 @@
"""Pythonic command-line interface parser that will make you smile.
* http://docopt.org
* Repository and issue-tracker: https://github.com/docopt/docopt
* Licensed under terms of MIT license (see LICENSE-MIT)
* Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com
"""
import sys
import re
__all__ = ['docopt']
__version__ = '0.6.2'
class DocoptLanguageError(Exception):
"""Error in construction of usage-message by developer."""
class DocoptExit(SystemExit):
"""Exit in case user invoked program with incorrect arguments."""
usage = ''
def __init__(self, message=''):
SystemExit.__init__(self, (message + '\n' + self.usage).strip())
class Pattern(object):
def __eq__(self, other):
return repr(self) == repr(other)
def __hash__(self):
return hash(repr(self))
def fix(self):
self.fix_identities()
self.fix_repeating_arguments()
return self
def fix_identities(self, uniq=None):
"""Make pattern-tree tips point to same object if they are equal."""
if not hasattr(self, 'children'):
return self
uniq = list(set(self.flat())) if uniq is None else uniq
for i, c in enumerate(self.children):
if not hasattr(c, 'children'):
assert c in uniq
self.children[i] = uniq[uniq.index(c)]
else:
c.fix_identities(uniq)
def fix_repeating_arguments(self):
"""Fix elements that should accumulate/increment values."""
either = [list(c.children) for c in self.either.children]
for case in either:
for e in [c for c in case if case.count(c) > 1]:
if type(e) is Argument or type(e) is Option and e.argcount:
if e.value is None:
e.value = []
elif type(e.value) is not list:
e.value = e.value.split()
if type(e) is Command or type(e) is Option and e.argcount == 0:
e.value = 0
return self
@property
def either(self):
"""Transform pattern into an equivalent, with only top-level Either."""
# Currently the pattern will not be equivalent, but more "narrow",
# although good enough to reason about list arguments.
ret = []
groups = [[self]]
while groups:
children = groups.pop(0)
types = [type(c) for c in children]
if Either in types:
either = [c for c in children if type(c) is Either][0]
children.pop(children.index(either))
for c in either.children:
groups.append([c] + children)
elif Required in types:
required = [c for c in children if type(c) is Required][0]
children.pop(children.index(required))
groups.append(list(required.children) + children)
elif Optional in types:
optional = [c for c in children if type(c) is Optional][0]
children.pop(children.index(optional))
groups.append(list(optional.children) + children)
elif AnyOptions in types:
optional = [c for c in children if type(c) is AnyOptions][0]
children.pop(children.index(optional))
groups.append(list(optional.children) + children)
elif OneOrMore in types:
oneormore = [c for c in children if type(c) is OneOrMore][0]
children.pop(children.index(oneormore))
groups.append(list(oneormore.children) * 2 + children)
else:
ret.append(children)
return Either(*[Required(*e) for e in ret])
class ChildPattern(Pattern):
def __init__(self, name, value=None):
self.name = name
self.value = value
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
def flat(self, *types):
return [self] if not types or type(self) in types else []
def match(self, left, collected=None):
collected = [] if collected is None else collected
pos, match = self.single_match(left)
if match is None:
return False, left, collected
left_ = left[:pos] + left[pos + 1:]
same_name = [a for a in collected if a.name == self.name]
if type(self.value) in (int, list):
if type(self.value) is int:
increment = 1
else:
increment = ([match.value] if type(match.value) is str
else match.value)
if not same_name:
match.value = increment
return True, left_, collected + [match]
same_name[0].value += increment
return True, left_, collected
return True, left_, collected + [match]
class ParentPattern(Pattern):
def __init__(self, *children):
self.children = list(children)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__,
', '.join(repr(a) for a in self.children))
def flat(self, *types):
if type(self) in types:
return [self]
return sum([c.flat(*types) for c in self.children], [])
class Argument(ChildPattern):
def single_match(self, left):
for n, p in enumerate(left):
if type(p) is Argument:
return n, Argument(self.name, p.value)
return None, None
@classmethod
def parse(class_, source):
name = re.findall('(<\S*?>)', source)[0]
value = re.findall('\[default: (.*)\]', source, flags=re.I)
return class_(name, value[0] if value else None)
class Command(Argument):
def __init__(self, name, value=False):
self.name = name
self.value = value
def single_match(self, left):
for n, p in enumerate(left):
if type(p) is Argument:
if p.value == self.name:
return n, Command(self.name, True)
else:
break
return None, None
class Option(ChildPattern):
def __init__(self, short=None, long=None, argcount=0, value=False):
assert argcount in (0, 1)
self.short, self.long = short, long
self.argcount, self.value = argcount, value
self.value = None if value is False and argcount else value
@classmethod
def parse(class_, option_description):
short, long, argcount, value = None, None, 0, False
options, _, description = option_description.strip().partition(' ')
options = options.replace(',', ' ').replace('=', ' ')
for s in options.split():
if s.startswith('--'):
long = s
elif s.startswith('-'):
short = s
else:
argcount = 1
if argcount:
matched = re.findall('\[default: (.*)\]', description, flags=re.I)
value = matched[0] if matched else None
return class_(short, long, argcount, value)
def single_match(self, left):
for n, p in enumerate(left):
if self.name == p.name:
return n, p
return None, None
@property
def name(self):
return self.long or self.short
def __repr__(self):
return 'Option(%r, %r, %r, %r)' % (self.short, self.long,
self.argcount, self.value)
class Required(ParentPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
l = left
c = collected
for p in self.children:
matched, l, c = p.match(l, c)
if not matched:
return False, left, collected
return True, l, c
class Optional(ParentPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
for p in self.children:
m, left, collected = p.match(left, collected)
return True, left, collected
class AnyOptions(Optional):
"""Marker/placeholder for [options] shortcut."""
class OneOrMore(ParentPattern):
def match(self, left, collected=None):
assert len(self.children) == 1
collected = [] if collected is None else collected
l = left
c = collected
l_ = None
matched = True
times = 0
while matched:
# could it be that something didn't match but changed l or c?
matched, l, c = self.children[0].match(l, c)
times += 1 if matched else 0
if l_ == l:
break
l_ = l
if times >= 1:
return True, l, c
return False, left, collected
class Either(ParentPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
outcomes = []
for p in self.children:
matched, _, _ = outcome = p.match(left, collected)
if matched:
outcomes.append(outcome)
if outcomes:
return min(outcomes, key=lambda outcome: len(outcome[1]))
return False, left, collected
class TokenStream(list):
def __init__(self, source, error):
self += source.split() if hasattr(source, 'split') else source
self.error = error
def move(self):
return self.pop(0) if len(self) else None
def current(self):
return self[0] if len(self) else None
def parse_long(tokens, options):
"""long ::= '--' chars [ ( ' ' | '=' ) chars ] ;"""
long, eq, value = tokens.move().partition('=')
assert long.startswith('--')
value = None if eq == value == '' else value
similar = [o for o in options if o.long == long]
if tokens.error is DocoptExit and similar == []: # if no exact match
similar = [o for o in options if o.long and o.long.startswith(long)]
if len(similar) > 1: # might be simply specified ambiguously 2+ times?
raise tokens.error('%s is not a unique prefix: %s?' %
(long, ', '.join(o.long for o in similar)))
elif len(similar) < 1:
argcount = 1 if eq == '=' else 0
o = Option(None, long, argcount)
options.append(o)
if tokens.error is DocoptExit:
o = Option(None, long, argcount, value if argcount else True)
else:
o = Option(similar[0].short, similar[0].long,
similar[0].argcount, similar[0].value)
if o.argcount == 0:
if value is not None:
raise tokens.error('%s must not have an argument' % o.long)
else:
if value is None:
if tokens.current() is None:
raise tokens.error('%s requires argument' % o.long)
value = tokens.move()
if tokens.error is DocoptExit:
o.value = value if value is not None else True
return [o]
def parse_shorts(tokens, options):
"""shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;"""
token = tokens.move()
assert token.startswith('-') and not token.startswith('--')
left = token.lstrip('-')
parsed = []
while left != '':
short, left = '-' + left[0], left[1:]
similar = [o for o in options if o.short == short]
if len(similar) > 1:
raise tokens.error('%s is specified ambiguously %d times' %
(short, len(similar)))
elif len(similar) < 1:
o = Option(short, None, 0)
options.append(o)
if tokens.error is DocoptExit:
o = Option(short, None, 0, True)
else: # why copying is necessary here?
o = Option(short, similar[0].long,
similar[0].argcount, similar[0].value)
value = None
if o.argcount != 0:
if left == '':
if tokens.current() is None:
raise tokens.error('%s requires argument' % short)
value = tokens.move()
else:
value = left
left = ''
if tokens.error is DocoptExit:
o.value = value if value is not None else True
parsed.append(o)
return parsed
def parse_pattern(source, options):
tokens = TokenStream(re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source),
DocoptLanguageError)
result = parse_expr(tokens, options)
if tokens.current() is not None:
raise tokens.error('unexpected ending: %r' % ' '.join(tokens))
return Required(*result)
def parse_expr(tokens, options):
"""expr ::= seq ( '|' seq )* ;"""
seq = parse_seq(tokens, options)
if tokens.current() != '|':
return seq
result = [Required(*seq)] if len(seq) > 1 else seq
while tokens.current() == '|':
tokens.move()
seq = parse_seq(tokens, options)
result += [Required(*seq)] if len(seq) > 1 else seq
return [Either(*result)] if len(result) > 1 else result
def parse_seq(tokens, options):
"""seq ::= ( atom [ '...' ] )* ;"""
result = []
while tokens.current() not in [None, ']', ')', '|']:
atom = parse_atom(tokens, options)
if tokens.current() == '...':
atom = [OneOrMore(*atom)]
tokens.move()
result += atom
return result
def parse_atom(tokens, options):
"""atom ::= '(' expr ')' | '[' expr ']' | 'options'
| long | shorts | argument | command ;
"""
token = tokens.current()
result = []
if token in '([':
tokens.move()
matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
result = pattern(*parse_expr(tokens, options))
if tokens.move() != matching:
raise tokens.error("unmatched '%s'" % token)
return [result]
elif token == 'options':
tokens.move()
return [AnyOptions()]
elif token.startswith('--') and token != '--':
return parse_long(tokens, options)
elif token.startswith('-') and token not in ('-', '--'):
return parse_shorts(tokens, options)
elif token.startswith('<') and token.endswith('>') or token.isupper():
return [Argument(tokens.move())]
else:
return [Command(tokens.move())]
def parse_argv(tokens, options, options_first=False):
"""Parse command-line argument vector.
If options_first:
argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
else:
argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
"""
parsed = []
while tokens.current() is not None:
if tokens.current() == '--':
return parsed + [Argument(None, v) for v in tokens]
elif tokens.current().startswith('--'):
parsed += parse_long(tokens, options)
elif tokens.current().startswith('-') and tokens.current() != '-':
parsed += parse_shorts(tokens, options)
elif options_first:
return parsed + [Argument(None, v) for v in tokens]
else:
parsed.append(Argument(None, tokens.move()))
return parsed
def parse_defaults(doc):
# in python < 2.7 you can't pass flags=re.MULTILINE
split = re.split('\n *(<\S+?>|-\S+?)', doc)[1:]
split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
options = [Option.parse(s) for s in split if s.startswith('-')]
#arguments = [Argument.parse(s) for s in split if s.startswith('<')]
#return options, arguments
return options
def printable_usage(doc):
# in python < 2.7 you can't pass flags=re.IGNORECASE
usage_split = re.split(r'([Uu][Ss][Aa][Gg][Ee]:)', doc)
if len(usage_split) < 3:
raise DocoptLanguageError('"usage:" (case-insensitive) not found.')
if len(usage_split) > 3:
raise DocoptLanguageError('More than one "usage:" (case-insensitive).')
return re.split(r'\n\s*\n', ''.join(usage_split[1:]))[0].strip()
def formal_usage(printable_usage):
pu = printable_usage.split()[1:] # split and drop "usage:"
return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )'
def extras(help, version, options, doc):
if help and any((o.name in ('-h', '--help')) and o.value for o in options):
print(doc.strip("\n"))
sys.exit()
if version and any(o.name == '--version' and o.value for o in options):
print(version)
sys.exit()
class Dict(dict):
def __repr__(self):
return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items()))
def docopt(doc, argv=None, help=True, version=None, options_first=False):
"""Parse `argv` based on command-line interface described in `doc`.
`docopt` creates your command-line interface based on its
description that you pass as `doc`. Such description can contain
--options, <positional-argument>, commands, which could be
[optional], (required), (mutually | exclusive) or repeated...
Parameters
----------
doc : str
Description of your command-line interface.
argv : list of str, optional
Argument vector to be parsed. sys.argv[1:] is used if not
provided.
help : bool (default: True)
Set to False to disable automatic help on -h or --help
options.
version : any object
If passed, the object will be printed if --version is in
`argv`.
options_first : bool (default: False)
Set to True to require options preceed positional arguments,
i.e. to forbid options and positional arguments intermix.
Returns
-------
args : dict
A dictionary, where keys are names of command-line elements
such as e.g. "--verbose" and "<path>", and values are the
parsed values of those elements.
Example
-------
>>> from docopt import docopt
>>> doc = '''
Usage:
my_program tcp <host> <port> [--timeout=<seconds>]
my_program serial <port> [--baud=<n>] [--timeout=<seconds>]
my_program (-h | --help | --version)
Options:
-h, --help Show this screen and exit.
--baud=<n> Baudrate [default: 9600]
'''
>>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']
>>> docopt(doc, argv)
{'--baud': '9600',
'--help': False,
'--timeout': '30',
'--version': False,
'<host>': '127.0.0.1',
'<port>': '80',
'serial': False,
'tcp': True}
See also
--------
* For video introduction see http://docopt.org
* Full documentation is available in README.rst as well as online
at https://github.com/docopt/docopt#readme
"""
if argv is None:
argv = sys.argv[1:]
DocoptExit.usage = printable_usage(doc)
options = parse_defaults(doc)
pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
# [default] syntax for argument is disabled
#for a in pattern.flat(Argument):
# same_name = [d for d in arguments if d.name == a.name]
# if same_name:
# a.value = same_name[0].value
argv = parse_argv(TokenStream(argv, DocoptExit), list(options),
options_first)
pattern_options = set(pattern.flat(Option))
for ao in pattern.flat(AnyOptions):
doc_options = parse_defaults(doc)
ao.children = list(set(doc_options) - pattern_options)
#if any_options:
# ao.children += [Option(o.short, o.long, o.argcount)
# for o in argv if type(o) is Option]
extras(help, version, argv, doc)
matched, left, collected = pattern.fix().match(argv)
if matched and left == []: # better error message if left?
return Dict((a.name, a.value) for a in (pattern.flat() + collected))
raise DocoptExit()
-202
View File
@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-5
View File
@@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
__author__ = 'Vadim Kravcenko'
__email__ = 'vadim.kravcenko@gmail.com'
__version__ = '0.4.10'
-1142
View File
File diff suppressed because it is too large Load Diff
-476
View File
@@ -1,476 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""pipreqs - Generate pip requirements.txt file based on imports
Usage:
pipreqs [options] [<path>]
Arguments:
<path> The path to the directory containing the application
files for which a requirements file should be
generated (defaults to the current working
directory).
Options:
--use-local Use ONLY local package info instead of querying PyPI.
--pypi-server <url> Use custom PyPi server.
--proxy <url> Use Proxy, parameter will be passed to requests
library. You can also just set the environments
parameter in your terminal:
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="https://10.10.1.10:1080"
--debug Print debug information.
--ignore <dirs>... Ignore extra directories, each separated by a comma.
--no-follow-links Do not follow symbolic links in the project
--encoding <charset> Use encoding parameter for file open
--savepath <file> Save the list of requirements in the given file
--print Output the list of requirements in the standard
output.
--force Overwrite existing requirements.txt
--diff <file> Compare modules in requirements.txt to project
imports.
--clean <file> Clean up requirements.txt by removing modules
that are not imported in project.
--no-pin Omit version of output packages.
"""
from __future__ import print_function, absolute_import
from contextlib import contextmanager
import os
import sys
import re
import logging
import codecs
import ast
import traceback
from pipenv.vendor.docopt import docopt
import pipenv.vendor.requests as requests
from pipenv.vendor.yarg import json2package
from pipenv.vendor.yarg.exceptions import HTTPError
from pipenv.vendor.pipreqs import __version__
REGEXP = [
re.compile(r'^import (.+)$'),
re.compile(r'^from ((?!\.+).*?) import (?:.*)$')
]
if sys.version_info[0] > 2:
open_func = open
py2 = False
else:
open_func = codecs.open
py2 = True
py2_exclude = ["concurrent", "concurrent.futures"]
@contextmanager
def _open(filename=None, mode='r'):
"""Open a file or ``sys.stdout`` depending on the provided filename.
Args:
filename (str): The path to the file that should be opened. If
``None`` or ``'-'``, ``sys.stdout`` or ``sys.stdin`` is
returned depending on the desired mode. Defaults to ``None``.
mode (str): The mode that should be used to open the file.
Yields:
A file handle.
"""
if not filename or filename == '-':
if not mode or 'r' in mode:
file = sys.stdin
elif 'w' in mode:
file = sys.stdout
else:
raise ValueError('Invalid mode for file: {}'.format(mode))
else:
file = open(filename, mode)
try:
yield file
finally:
if file not in (sys.stdin, sys.stdout):
file.close()
def get_all_imports(
path, encoding=None, extra_ignore_dirs=None, follow_links=True):
imports = set()
raw_imports = set()
candidates = []
ignore_errors = False
ignore_dirs = [".hg", ".svn", ".git", ".tox", "__pycache__", "env", "venv"]
if extra_ignore_dirs:
ignore_dirs_parsed = []
for e in extra_ignore_dirs:
ignore_dirs_parsed.append(os.path.basename(os.path.realpath(e)))
ignore_dirs.extend(ignore_dirs_parsed)
walk = os.walk(path, followlinks=follow_links)
for root, dirs, files in walk:
dirs[:] = [d for d in dirs if d not in ignore_dirs]
candidates.append(os.path.basename(root))
files = [fn for fn in files if os.path.splitext(fn)[1] == ".py"]
candidates += [os.path.splitext(fn)[0] for fn in files]
for file_name in files:
file_name = os.path.join(root, file_name)
with open_func(file_name, "r", encoding=encoding) as f:
contents = f.read()
try:
tree = ast.parse(contents)
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for subnode in node.names:
raw_imports.add(subnode.name)
elif isinstance(node, ast.ImportFrom):
raw_imports.add(node.module)
except Exception as exc:
if ignore_errors:
traceback.print_exc(exc)
logging.warn("Failed on file: %s" % file_name)
continue
else:
logging.error("Failed on file: %s" % file_name)
raise exc
# Clean up imports
for name in [n for n in raw_imports if n]:
# Sanity check: Name could have been None if the import
# statement was as ``from . import X``
# Cleanup: We only want to first part of the import.
# Ex: from django.conf --> django.conf. But we only want django
# as an import.
cleaned_name, _, _ = name.partition('.')
imports.add(cleaned_name)
packages = imports - (set(candidates) & imports)
logging.debug('Found packages: {0}'.format(packages))
with open(join("stdlib"), "r") as f:
data = {x.strip() for x in f}
data = {x for x in data if x not in py2_exclude} if py2 else data
return list(packages - data)
def filter_line(l):
return len(l) > 0 and l[0] != "#"
def generate_requirements_file(path, imports):
with _open(path, "w") as out_file:
logging.debug('Writing {num} requirements: {imports} to {file}'.format(
num=len(imports),
file=path,
imports=", ".join([x['name'] for x in imports])
))
fmt = '{name}=={version}'
out_file.write('\n'.join(
fmt.format(**item) if item['version'] else '{name}'.format(**item)
for item in imports) + '\n')
def output_requirements(imports):
generate_requirements_file('-', imports)
def get_imports_info(
imports, pypi_server="https://pypi.python.org/pypi/", proxy=None):
result = []
for item in imports:
try:
response = requests.get(
"{0}{1}/json".format(pypi_server, item), proxies=proxy)
if response.status_code == 200:
if hasattr(response.content, 'decode'):
data = json2package(response.content.decode())
else:
data = json2package(response.content)
elif response.status_code >= 300:
raise HTTPError(status_code=response.status_code,
reason=response.reason)
except HTTPError:
logging.debug(
'Package %s does not exist or network problems', item)
continue
result.append({'name': item, 'version': data.latest_release_id})
return result
def get_locally_installed_packages(encoding=None):
packages = {}
ignore = ["tests", "_tests", "egg", "EGG", "info"]
for path in sys.path:
for root, dirs, files in os.walk(path):
for item in files:
if "top_level" in item:
item = os.path.join(root, item)
with open_func(item, "r", encoding=encoding) as f:
package = root.split(os.sep)[-1].split("-")
try:
package_import = f.read().strip().split("\n")
except: # NOQA
# TODO: What errors do we intend to suppress here?
continue
for i_item in package_import:
if ((i_item not in ignore) and
(package[0] not in ignore)):
version = None
if len(package) > 1:
version = package[1].replace(
".dist", "").replace(".egg", "")
packages[i_item] = {
'version': version,
'name': package[0]
}
return packages
def get_import_local(imports, encoding=None):
local = get_locally_installed_packages()
result = []
for item in imports:
if item.lower() in local:
result.append(local[item.lower()])
# removing duplicates of package/version
result_unique = [
dict(t)
for t in set([
tuple(d.items()) for d in result
])
]
return result_unique
def get_pkg_names(pkgs):
"""Get PyPI package names from a list of imports.
Args:
pkgs (List[str]): List of import names.
Returns:
List[str]: The corresponding PyPI package names.
"""
result = set()
with open(join("mapping"), "r") as f:
data = dict(x.strip().split(":") for x in f)
for pkg in pkgs:
# Look up the mapped requirement. If a mapping isn't found,
# simply use the package name.
result.add(data.get(pkg, pkg))
# Return a sorted list for backward compatibility.
return sorted(result, key=lambda s: s.lower())
def get_name_without_alias(name):
if "import " in name:
match = REGEXP[0].match(name.strip())
if match:
name = match.groups(0)[0]
return name.partition(' as ')[0].partition('.')[0].strip()
def join(f):
return os.path.join(os.path.dirname(__file__), f)
def parse_requirements(file_):
"""Parse a requirements formatted file.
Traverse a string until a delimiter is detected, then split at said
delimiter, get module name by element index, create a dict consisting of
module:version, and add dict to list of parsed modules.
Args:
file_: File to parse.
Raises:
OSerror: If there's any issues accessing the file.
Returns:
tuple: The contents of the file, excluding comments.
"""
modules = []
# For the dependency identifier specification, see
# https://www.python.org/dev/peps/pep-0508/#complete-grammar
delim = ["<", ">", "=", "!", "~"]
try:
f = open_func(file_, "r")
except OSError:
logging.error("Failed on file: {}".format(file_))
raise
else:
try:
data = [x.strip() for x in f.readlines() if x != "\n"]
finally:
f.close()
data = [x for x in data if x[0].isalpha()]
for x in data:
# Check for modules w/o a specifier.
if not any([y in x for y in delim]):
modules.append({"name": x, "version": None})
for y in x:
if y in delim:
module = x.split(y)
module_name = module[0]
module_version = module[-1].replace("=", "")
module = {"name": module_name, "version": module_version}
if module not in modules:
modules.append(module)
break
return modules
def compare_modules(file_, imports):
"""Compare modules in a file to imported modules in a project.
Args:
file_ (str): File to parse for modules to be compared.
imports (tuple): Modules being imported in the project.
Returns:
tuple: The modules not imported in the project, but do exist in the
specified file.
"""
modules = parse_requirements(file_)
imports = [imports[i]["name"] for i in range(len(imports))]
modules = [modules[i]["name"] for i in range(len(modules))]
modules_not_imported = set(modules) - set(imports)
return modules_not_imported
def diff(file_, imports):
"""Display the difference between modules in a file and imported modules.""" # NOQA
modules_not_imported = compare_modules(file_, imports)
logging.info(
"The following modules are in {} but do not seem to be imported: "
"{}".format(file_, ", ".join(x for x in modules_not_imported)))
def clean(file_, imports):
"""Remove modules that aren't imported in project from file."""
modules_not_imported = compare_modules(file_, imports)
re_remove = re.compile("|".join(modules_not_imported))
to_write = []
try:
f = open_func(file_, "r+")
except OSError:
logging.error("Failed on file: {}".format(file_))
raise
else:
try:
for i in f.readlines():
if re_remove.match(i) is None:
to_write.append(i)
f.seek(0)
f.truncate()
for i in to_write:
f.write(i)
finally:
f.close()
logging.info("Successfully cleaned up requirements in " + file_)
def init(args):
encoding = args.get('--encoding')
extra_ignore_dirs = args.get('--ignore')
follow_links = not args.get('--no-follow-links')
input_path = args['<path>']
if input_path is None:
input_path = os.path.abspath(os.curdir)
if extra_ignore_dirs:
extra_ignore_dirs = extra_ignore_dirs.split(',')
candidates = get_all_imports(input_path,
encoding=encoding,
extra_ignore_dirs=extra_ignore_dirs,
follow_links=follow_links)
candidates = get_pkg_names(candidates)
logging.debug("Found imports: " + ", ".join(candidates))
pypi_server = "https://pypi.python.org/pypi/"
proxy = None
if args["--pypi-server"]:
pypi_server = args["--pypi-server"]
if args["--proxy"]:
proxy = {'http': args["--proxy"], 'https': args["--proxy"]}
if args["--use-local"]:
logging.debug(
"Getting package information ONLY from local installation.")
imports = get_import_local(candidates, encoding=encoding)
else:
logging.debug("Getting packages information from Local/PyPI")
local = get_import_local(candidates, encoding=encoding)
# Get packages that were not found locally
difference = [x for x in candidates
if x.lower() not in [z['name'].lower() for z in local]]
imports = local + get_imports_info(difference,
proxy=proxy,
pypi_server=pypi_server)
path = (args["--savepath"] if args["--savepath"] else
os.path.join(input_path, "requirements.txt"))
if args["--diff"]:
diff(args["--diff"], imports)
return
if args["--clean"]:
clean(args["--clean"], imports)
return
if (not args["--print"]
and not args["--savepath"]
and not args["--force"]
and os.path.exists(path)):
logging.warning("Requirements.txt already exists, "
"use --force to overwrite it")
return
if args.get('--no-pin'):
imports = [{'name': item["name"], 'version': ''} for item in imports]
if args["--print"]:
output_requirements(imports)
logging.info("Successfully output requirements")
else:
generate_requirements_file(path, imports)
logging.info("Successfully saved requirements file in " + path)
def main(): # pragma: no cover
args = docopt(__doc__, version=__version__)
log_level = logging.DEBUG if args['--debug'] else logging.INFO
logging.basicConfig(level=log_level, format='%(levelname)s: %(message)s')
try:
init(args)
except KeyboardInterrupt:
sys.exit(0)
if __name__ == '__main__':
main() # pragma: no cover
-495
View File
@@ -1,495 +0,0 @@
__builtin__
__future__
__main__
_dummy_thread
_thread
_winreg
abc
aepack
aetools
aetypes
aifc
AL
al
anydbm
applesingle
argparse
array
ast
asynchat
asyncio
asyncore
atexit
audioop
autoGIL
base64
BaseHTTPServer
Bastion
bdb
binascii
binhex
bisect
bsddb
buildtools
builtins
bz2
calendar
Carbon
Carbon.AE
Carbon.AH
Carbon.App
Carbon.Appearance
Carbon.CarbonEvents
Carbon.CarbonEvt
Carbon.CF
Carbon.CG
Carbon.Cm
Carbon.Components
Carbon.ControlAccessor
Carbon.Controls
Carbon.CoreFounation
Carbon.CoreGraphics
Carbon.Ctl
Carbon.Dialogs
Carbon.Dlg
Carbon.Drag
Carbon.Dragconst
Carbon.Events
Carbon.Evt
Carbon.File
Carbon.Files
Carbon.Fm
Carbon.Folder
Carbon.Folders
Carbon.Fonts
Carbon.Help
Carbon.IBCarbon
Carbon.IBCarbonRuntime
Carbon.Icns
Carbon.Icons
Carbon.Launch
Carbon.LaunchServices
Carbon.List
Carbon.Lists
Carbon.MacHelp
Carbon.MediaDescr
Carbon.Menu
Carbon.Menus
Carbon.Mlte
Carbon.OSA
Carbon.OSAconst
Carbon.Qd
Carbon.Qdoffs
Carbon.QDOffscreen
Carbon.Qt
Carbon.QuickDraw
Carbon.QuickTime
Carbon.Res
Carbon.Resources
Carbon.Scrap
Carbon.Snd
Carbon.Sound
Carbon.TE
Carbon.TextEdit
Carbon.Win
Carbon.Windows
cd
cfmfile
cgi
CGIHTTPServer
cgitb
chunk
cmath
cmd
code
codecs
codeop
collections
collections.abc
ColorPicker
colorsys
commands
compileall
compiler
compiler.ast
compiler.visitor
concurrent
concurrent.futures
ConfigParser
configparser
contextlib
Cookie
cookielib
copy
copy_reg
copyreg
cPickle
cProfile
crypt
cStringIO
csv
ctypes
curses
curses.ascii
curses.panel
curses.textpad
datetime
dbhash
dbm
dbm.dumb
dbm.gnu
dbm.ndbm
decimal
DEVICE
difflib
dircache
dis
distutils
distutils.archive_util
distutils.bcppcompiler
distutils.ccompiler
distutils.cmd
distutils.command
distutils.command.bdist
distutils.command.bdist_dumb
distutils.command.bdist_msi
distutils.command.bdist_packager
distutils.command.bdist_rpm
distutils.command.bdist_wininst
distutils.command.build
distutils.command.build_clib
distutils.command.build_ext
distutils.command.build_py
distutils.command.build_scripts
distutils.command.check
distutils.command.clean
distutils.command.config
distutils.command.install
distutils.command.install_data
distutils.command.install_headers
distutils.command.install_lib
distutils.command.install_scripts
distutils.command.register
distutils.command.sdist
distutils.core
distutils.cygwinccompiler
distutils.debug
distutils.dep_util
distutils.dir_util
distutils.dist
distutils.emxccompiler
distutils.errors
distutils.extension
distutils.fancy_getopt
distutils.file_util
distutils.filelist
distutils.log
distutils.msvccompiler
distutils.spawn
distutils.sysconfig
distutils.text_file
distutils.unixccompiler
distutils.util
distutils.version
dl
doctest
DocXMLRPCServer
dumbdbm
dummy_thread
dummy_threading
EasyDialogs
email
email.charset
email.contentmanager
email.encoders
email.errors
email.generator
email.header
email.headerregistry
email.iterators
email.message
email.mime
email.parser
email.policy
email.utils
encodings
encodings.idna
encodings.mbcs
encodings.utf_8_sig
ensurepip
enum
errno
exceptions
faulthandler
fcntl
filecmp
fileinput
findertools
FL
fl
flp
fm
fnmatch
formatter
fpectl
fpformat
fractions
FrameWork
ftplib
functools
future_builtins
gc
gdbm
gensuitemodule
getopt
getpass
gettext
GL
gl
glob
grp
gzip
hashlib
heapq
hmac
hotshot
hotshot.stats
html
html.entities
html.parser
htmlentitydefs
htmllib
HTMLParser
http
http.client
http.cookiejar
http.cookies
http.server
httplib
ic
icopen
imageop
imaplib
imgfile
imghdr
imp
importlib
importlib.abc
importlib.machinery
importlib.util
imputil
inspect
io
ipaddress
itertools
jpeg
json
json.tool
keyword
lib2to3
linecache
locale
logging
logging.config
logging.handlers
lzma
macerrors
MacOS
macostools
macpath
macresource
mailbox
mailcap
marshal
math
md5
mhlib
mimetools
mimetypes
MimeWriter
mimify
MiniAEFrame
mmap
modulefinder
msilib
msvcrt
multifile
multiprocessing
multiprocessing.connection
multiprocessing.dummy
multiprocessing.managers
multiprocessing.pool
multiprocessing.sharedctypes
mutex
Nav
netrc
new
nis
nntplib
numbers
operator
optparse
os
os.path
ossaudiodev
parser
pathlib
pdb
pickle
pickletools
pipes
PixMapWrapper
pkg_resources
pkgutil
platform
plistlib
popen2
poplib
posix
posixfile
pprint
profile
pstats
pty
pwd
py_compile
pyclbr
pydoc
Queue
queue
quopri
random
re
readline
reprlib
resource
rexec
rfc822
rlcompleter
robotparser
runpy
sched
ScrolledText
select
selectors
sets
setuptools
sgmllib
sha
shelve
shlex
shutil
signal
SimpleHTTPServer
SimpleXMLRPCServer
site
smtpd
smtplib
sndhdr
socket
SocketServer
socketserver
spwd
sqlite3
ssl
stat
statistics
statvfs
string
StringIO
stringprep
struct
subprocess
sunau
SUNAUDIODEV
sunaudiodev
symbol
symtable
sys
sysconfig
syslog
tabnanny
tarfile
telnetlib
tempfile
termios
test
test.support
test.test_support
textwrap
thread
threading
time
timeit
Tix
Tkinter
tkinter
tkinter.scrolledtext
tkinter.tix
tkinter.ttk
token
tokenize
trace
traceback
tracemalloc
ttk
tty
turtle
turtledemo
types
typing
unicodedata
unittest
unittest.mock
urllib
urllib.error
urllib.parse
urllib.request
urllib.response
urllib.robotparser
urllib2
urlparse
user
UserDict
UserList
UserString
uu
uuid
venv
videoreader
W
warnings
wave
weakref
webbrowser
whichdb
winreg
winsound
wsgiref
wsgiref.handlers
wsgiref.headers
wsgiref.simple_server
wsgiref.util
wsgiref.validate
xdrlib
xml
xml.dom
xml.dom.minidom
xml.dom.pulldom
xml.etree.ElementTree
xml.parsers.expat
xml.parsers.expat.errors
xml.parsers.expat.model
xml.sax
xml.sax.handler
xml.sax.saxutils
xml.sax.xmlreader
xmlrpc
xmlrpc.client
xmlrpc.server
xmlrpclib
yp
zipapp
zipfile
zipimport
zlib
-3
View File
@@ -8,7 +8,6 @@ click-didyoumean==0.0.3
click==8.0.3
colorama==0.4.4
distlib==0.3.2
docopt==0.6.2
dparse==0.5.1
funcsigs==1.0.2
idna==3.2
@@ -24,7 +23,6 @@ pep517==0.11.0
pexpect==4.8.0
pip-shims==0.6.0
pipdeptree==2.0.0
pipreqs==0.4.10
platformdirs==2.4.0
plette[validation]==0.2.3
ptyprocess==0.7.0
@@ -43,6 +41,5 @@ tomlkit==0.7.2
urllib3==1.26.6
vistir==0.5.2
wheel==0.36.2
yarg==0.1.9
yaspin==2.0.0
zipp==3.5.0
-20
View File
@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Kura
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.
-13
View File
@@ -1,13 +0,0 @@
Copyright 2014 Kenneth Reitz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-7
View File
@@ -1,7 +0,0 @@
__title__ = 'yarg'
__version__ = '0.1.9'
__author__ = 'Kura'
__email__ = 'kura@kura.io'
__url__ = 'https://yarg.readthedocs.org/'
__license__ = 'MIT'
__copyright__ = 'Copyright 2014 Kura'
-56
View File
@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
# (The MIT License)
#
# Copyright (c) 2014 Kura
#
# 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.
"""
yarg(1) -- A semi hard Cornish cheese, also queries PyPI
========================================================
Yarg is a PyPI client.
>>> import yarg
>>>
>>> package = yarg.get("yarg")
>>> package.name
u'yarg'
>>> package.author
Author(name=u'Kura', email=u'kura@kura.io')
>>>
>>> yarg.newest_packages()
[<Package yarg>, <Package gray>, <Package ragy>]
>>>
>>> yarg.latest_updated_packages()
[<Package yarg>, <Package gray>, <Package ragy>]
Full documentation is at <https://yarg.readthedocs.org>.
"""
from .client import get
from .exceptions import HTTPError
from .package import json2package
from .parse import (newest_packages, latest_updated_packages)
__all__ = ['get', 'HTTPError', 'json2package', 'newest_packages',
'latest_updated_packages', ]
-54
View File
@@ -1,54 +0,0 @@
# -*- coding: utf-8 -*-
# (The MIT License)
#
# Copyright (c) 2014 Kura
#
# 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.
import pipenv.vendor.requests as requests
from .exceptions import HTTPError
from .package import json2package
def get(package_name, pypi_server="https://pypi.python.org/pypi/"):
"""
Constructs a request to the PyPI server and returns a
:class:`yarg.package.Package`.
:param package_name: case sensitive name of the package on the PyPI server.
:param pypi_server: (option) URL to the PyPI server.
>>> import yarg
>>> package = yarg.get('yarg')
<Package yarg>
"""
if not pypi_server.endswith("/"):
pypi_server = pypi_server + "/"
response = requests.get("{0}{1}/json".format(pypi_server,
package_name))
if response.status_code >= 300:
raise HTTPError(status_code=response.status_code,
reason=response.reason)
if hasattr(response.content, 'decode'):
return json2package(response.content.decode())
else:
return json2package(response.content)
-58
View File
@@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
# (The MIT License)
#
# Copyright (c) 2014 Kura
#
# 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.
from pipenv.vendor.requests.exceptions import HTTPError as RHTTPError
class YargException(Exception):
pass
class HTTPError(YargException, RHTTPError):
"""
A catchall HTTPError exception to handle HTTP errors
when using :meth:`yarg.get`.
This exception is also loaded at :class:`yarg.HTTPError`
for ease of access.
:member: status_code
"""
def __init__(self, *args, **kwargs):
for key, val in kwargs.items():
setattr(self, key, val)
if hasattr(self, 'status_code'):
setattr(self, 'errno', self.status_code)
if hasattr(self, 'reason'):
setattr(self, 'message', self.reason)
def __str__(self):
return self.__repr__()
def __repr__(self):
if hasattr(self, 'status_code') and hasattr(self, 'reason'):
return "<HTTPError {0} {1}>".format(self.status_code, self.reason)
return "<HTTPError>"
-324
View File
@@ -1,324 +0,0 @@
# -*- coding: utf-8 -*-
# (The MIT License)
#
# Copyright (c) 2014 Kura
#
# 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.
try:
import simplejson as json
except ImportError:
import json
from collections import namedtuple
import re
from .release import Release
class Package(object):
"""
A PyPI package.
:param pypi_dict: A dictionary retrieved from the PyPI server.
"""
def __init__(self, pypi_dict):
self._package = pypi_dict['info']
self._releases = pypi_dict['releases']
def __repr__(self):
return "<Package {0}>".format(self.name)
@property
def name(self):
"""
>>> package = yarg.get('yarg')
>>> package.name
u'yarg'
"""
return self._package['name']
@property
def pypi_url(self):
"""
>>> package = yarg.get('yarg')
>>> package.url
u'https://pypi.python.org/pypi/yarg'
"""
return self._package['package_url']
@property
def summary(self):
"""
>>> package = yarg.get('yarg')
>>> package.summary
u'Some random summary stuff'
"""
return self._package['summary']
@property
def description(self):
"""
>>> package = yarg.get('yarg')
>>> package.description
u'A super long description, usually uploaded from the README'
"""
return self._package['description']
@property
def homepage(self):
"""
>>> package = yarg.get('yarg')
>>> package.homepage
u'https://kura.io/yarg/'
"""
if ('home_page' not in self._package or
self._package['home_page'] == ""):
return None
return self._package['home_page']
@property
def bugtracker(self):
"""
>>> package = yarg.get('yarg')
>>> package.bugtracker
u'https://github.com/kura/yarg/issues'
"""
if ('bugtrack_url' not in self._package or
self._package['bugtrack_url'] == ""):
return None
return self._package['bugtrack_url']
@property
def docs(self):
"""
>>> package = yarg.get('yarg')
>>> package.docs
u'https://yarg.readthedocs.org/en/latest'
"""
if ('docs_url' not in self._package or
self._package['docs_url'] == ""):
return None
return self._package['docs_url']
@property
def author(self):
"""
>>> package = yarg.get('yarg')
>>> package.author
Author(name=u'Kura', email=u'kura@kura.io')
"""
author = namedtuple('Author', 'name email')
return author(name=self._package['author'],
email=self._package['author_email'])
@property
def maintainer(self):
"""
>>> package = yarg.get('yarg')
>>> package.maintainer
Maintainer(name=u'Kura', email=u'kura@kura.io')
"""
maintainer = namedtuple('Maintainer', 'name email')
return maintainer(name=self._package['maintainer'],
email=self._package['maintainer_email'])
@property
def license(self):
"""
>>> package = yarg.get('yarg')
>>> package.license
u'MIT'
"""
return self._package['license']
@property
def license_from_classifiers(self):
"""
>>> package = yarg.get('yarg')
>>> package.license_from_classifiers
u'MIT License'
"""
if len(self.classifiers) > 0:
for c in self.classifiers:
if c.startswith("License"):
return c.split(" :: ")[-1]
@property
def downloads(self):
"""
>>> package = yarg.get('yarg')
>>> package.downloads
Downloads(day=50100, week=367941, month=1601938) # I wish
"""
_downloads = self._package['downloads']
downloads = namedtuple('Downloads', 'day week month')
return downloads(day=_downloads['last_day'],
week=_downloads['last_week'],
month=_downloads['last_month'])
@property
def classifiers(self):
"""
>>> package = yarg.get('yarg')
>>> package.classifiers
[u'License :: OSI Approved :: MIT License',
u'Programming Language :: Python :: 2.7',
u'Programming Language :: Python :: 3.4']
"""
return self._package['classifiers']
@property
def python_versions(self):
"""
Returns a list of Python version strings that
the package has listed in :attr:`yarg.Release.classifiers`.
>>> package = yarg.get('yarg')
>>> package.python_versions
[u'2.6', u'2.7', u'3.3', u'3.4']
"""
version_re = re.compile(r"""Programming Language \:\: """
"""Python \:\: \d\.\d""")
return [c.split(' :: ')[-1] for c in self.classifiers
if version_re.match(c)]
@property
def python_implementations(self):
"""
Returns a list of Python implementation strings that
the package has listed in :attr:`yarg.Release.classifiers`.
>>> package = yarg.get('yarg')
>>> package.python_implementations
[u'CPython', u'PyPy']
"""
return [c.split(' :: ')[-1] for c in self.classifiers
if c.startswith("""Programming Language :: """
"""Python :: Implementation""")]
@property
def latest_release_id(self):
"""
>>> package = yarg.get('yarg')
>>> package.latest_release_id
u'0.1.0'
"""
return self._package['version']
@property
def latest_release(self):
"""
A list of :class:`yarg.release.Release` objects for each file in the
latest release.
>>> package = yarg.get('yarg')
>>> package.latest_release
[<Release 0.1.0>, <Release 0.1.0>]
"""
release_id = self.latest_release_id
return self.release(release_id)
@property
def has_wheel(self):
"""
Returns `True` if one of the :class:`yarg.release.Release` objects
in the latest set of release files is `wheel` format. Returns
`False` if not.
>>> package = yarg.get('yarg')
>>> package.has_wheel
True
"""
for release in self.latest_release:
if release.package_type in ('wheel', 'bdist_wheel'):
return True
return False
@property
def has_egg(self):
"""
Returns `True` if one of the :class:`yarg.release.Release` objects
in the latest set of release files is `egg` format. Returns
`False` if not.
>>> package = yarg.get('yarg')
>>> package.has_egg
False
"""
for release in self.latest_release:
if release.package_type in ('egg', 'bdist_egg'):
return True
return False
@property
def has_source(self):
"""
Returns `True` if one of the :class:`yarg.release.Release` objects
in the latest set of release files is `source` format. Returns
`False` if not.
>>> package = yarg.get('yarg')
>>> package.has_source
True
"""
for release in self.latest_release:
if release.package_type in ('source', 'sdist'):
return True
return False
@property
def release_ids(self):
"""
>>> package = yarg.get('yarg')
>>> package.release_ids
[u'0.0.1', u'0.0.5', u'0.1.0']
"""
r = [(k, self._releases[k][0]['upload_time'])
for k in self._releases.keys()
if len(self._releases[k]) > 0]
return [k[0] for k in sorted(r, key=lambda k: k[1])]
def release(self, release_id):
"""
A list of :class:`yarg.release.Release` objects for each file in a
release.
:param release_id: A pypi release id.
>>> package = yarg.get('yarg')
>>> last_release = yarg.releases[-1]
>>> package.release(last_release)
[<Release 0.1.0>, <Release 0.1.0>]
"""
if release_id not in self.release_ids:
return None
return [Release(release_id, r) for r in self._releases[release_id]]
def json2package(json_content):
"""
Returns a :class:`yarg.release.Release` object from JSON content from the
PyPI server.
:param json_content: JSON encoded content from the PyPI server.
"""
return Package(json.loads(json_content))
-172
View File
@@ -1,172 +0,0 @@
# -*- coding: utf-8 -*-
# (The MIT License)
#
# Copyright (c) 2014 Kura
#
# 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.
from datetime import datetime
import xml.etree.ElementTree
import pipenv.vendor.requests as requests
from .exceptions import HTTPError
def _get(pypi_server):
"""
Query the PyPI RSS feed and return a list
of XML items.
"""
response = requests.get(pypi_server)
if response.status_code >= 300:
raise HTTPError(status_code=response.status_code,
reason=response.reason)
if hasattr(response.content, 'decode'):
tree = xml.etree.ElementTree.fromstring(response.content.decode())
else:
tree = xml.etree.ElementTree.fromstring(response.content)
channel = tree.find('channel')
return channel.findall('item')
def newest_packages(
pypi_server="https://pypi.python.org/pypi?%3Aaction=packages_rss"):
"""
Constructs a request to the PyPI server and returns a list of
:class:`yarg.parse.Package`.
:param pypi_server: (option) URL to the PyPI server.
>>> import yarg
>>> yarg.newest_packages()
[<Package yarg>, <Package gray>, <Package ragy>]
"""
items = _get(pypi_server)
i = []
for item in items:
i_dict = {'name': item[0].text.split()[0],
'url': item[1].text,
'description': item[3].text,
'date': item[4].text}
i.append(Package(i_dict))
return i
def latest_updated_packages(
pypi_server="https://pypi.python.org/pypi?%3Aaction=rss"):
"""
Constructs a request to the PyPI server and returns a list of
:class:`yarg.parse.Package`.
:param pypi_server: (option) URL to the PyPI server.
>>> import yarg
>>> yarg.latest_updated_packages()
[<Package yarg>, <Package gray>, <Package ragy>]
"""
items = _get(pypi_server)
i = []
for item in items:
name, version = item[0].text.split()
i_dict = {'name': name,
'version': version,
'url': item[1].text,
'description': item[2].text,
'date': item[3].text}
i.append(Package(i_dict))
return i
class Package(object):
"""
A PyPI package generated from the RSS feed information.
:param pypi_dict: A dictionary retrieved from the PyPI server.
"""
def __init__(self, pypi_dict):
self._content = pypi_dict
def __repr__(self):
return "<Package {0}>".format(self.name)
@property
def name(self):
"""
>>> package = yarg.newest_packages()[0]
>>> package.name
u'yarg'
>>> package = yarg.latest_updated_packages()[0]
>>> package.name
u'yarg'
"""
return self._content['name']
@property
def version(self):
"""
>>> package = yarg.newest_packages()[0]
>>> package.name
u'yarg'
>>> package = yarg.latest_updated_packages()[0]
>>> package.name
u'yarg'
"""
if 'version' not in self._content:
return None
return self._content['version']
@property
def url(self):
"""
This is only available for :meth:`yarg.latest_updated_packages`, for
:meth:`yarg.newest_packages` will return `None`
>>> package = yarg.latest_updated_packages()[0]
>>> package.url
u'http://pypi.python.org/pypi/yarg'
"""
return self._content['url']
@property
def date(self):
"""
>>> package = yarg.newest_packages()[0]
>>> package.date
datetime.datetime(2014, 8, 9, 8, 40, 20)
>>> package = yarg.latest_updated_packages()[0]
>>> package.date
datetime.datetime(2014, 8, 9, 8, 40, 20)
"""
return datetime.strptime(self._content['date'],
"%d %b %Y %H:%M:%S %Z")
@property
def description(self):
"""
>>> package = yarg.newest_packages()[0]
>>> package.description
u'Some random summary stuff'
>>> package = yarg.latest_updated_packages()[0]
>>> package.description
u'Some random summary stuff'
"""
return self._content['description']
-147
View File
@@ -1,147 +0,0 @@
# -*- coding: utf-8 -*-
# (The MIT License)
#
# Copyright (c) 2014 Kura
#
# 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.
from datetime import datetime
class Release(object):
"""
A release file from PyPI.
:param release_id: A release id.
:param pypi_dict: A dictionary of a release file.
"""
def __init__(self, release_id, pypi_dict):
self._release = pypi_dict
self._release['release_id'] = release_id
def __repr__(self):
return "<Release {0}>".format(self.release_id)
@property
def release_id(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r[0].release_id
u'0.1.0'
"""
return self._release['release_id']
@property
def uploaded(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.uploaded
datetime.datime(2014, 8, 7, 21, 26, 19)
"""
return datetime.strptime(self._release['upload_time'],
'%Y-%m-%dT%H:%M:%S')
@property
def python_version(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.python_version
u'2.7'
"""
return self._release['python_version']
@property
def url(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.url
u'https://pypi.python.org/packages/2.7/y/yarg/yarg...'
"""
return self._release['url']
@property
def md5_digest(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.md5_digest
u'bec88e1c1765ca6177360e8f37b44c5c'
"""
return self._release['md5_digest']
@property
def filename(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.filename
u'yarg-0.1.0-py27-none-any.whl'
"""
return self._release['filename']
@property
def size(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.size
52941
"""
return self._release['size']
@property
def package_type(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.package_type
u'wheel'
"""
mapping = {'bdist_egg': u'egg', 'bdist_wheel': u'wheel',
'sdist': u'source'}
ptype = self._release['packagetype']
if ptype in mapping.keys():
return mapping[ptype]
return ptype
@property
def has_sig(self):
"""
>>> package = yarg.get('yarg')
>>> v = "0.1.0"
>>> r = package.release(v)
>>> r.has_sig
True
"""
return self._release['has_sig']