Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
news
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Sartika Aritonang
news
Commits
706872b7
Commit
706872b7
authored
May 29, 2020
by
Sartika Aritonang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Upload New File
parent
b716502e
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
516 additions
and
0 deletions
+516
-0
index.py
stbi/Lib/site-packages/pip/_vendor/distlib/index.py
+516
-0
No files found.
stbi/Lib/site-packages/pip/_vendor/distlib/index.py
0 → 100644
View file @
706872b7
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 Vinay Sajip.
# Licensed to the Python Software Foundation under a contributor agreement.
# See LICENSE.txt and CONTRIBUTORS.txt.
#
import
hashlib
import
logging
import
os
import
shutil
import
subprocess
import
tempfile
try
:
from
threading
import
Thread
except
ImportError
:
from
dummy_threading
import
Thread
from
.
import
DistlibException
from
.compat
import
(
HTTPBasicAuthHandler
,
Request
,
HTTPPasswordMgr
,
urlparse
,
build_opener
,
string_types
)
from
.util
import
cached_property
,
zip_dir
,
ServerProxy
logger
=
logging
.
getLogger
(
__name__
)
DEFAULT_INDEX
=
'https://pypi.org/pypi'
DEFAULT_REALM
=
'pypi'
class
PackageIndex
(
object
):
"""
This class represents a package index compatible with PyPI, the Python
Package Index.
"""
boundary
=
b
'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$'
def
__init__
(
self
,
url
=
None
):
"""
Initialise an instance.
:param url: The URL of the index. If not specified, the URL for PyPI is
used.
"""
self
.
url
=
url
or
DEFAULT_INDEX
self
.
read_configuration
()
scheme
,
netloc
,
path
,
params
,
query
,
frag
=
urlparse
(
self
.
url
)
if
params
or
query
or
frag
or
scheme
not
in
(
'http'
,
'https'
):
raise
DistlibException
(
'invalid repository:
%
s'
%
self
.
url
)
self
.
password_handler
=
None
self
.
ssl_verifier
=
None
self
.
gpg
=
None
self
.
gpg_home
=
None
with
open
(
os
.
devnull
,
'w'
)
as
sink
:
# Use gpg by default rather than gpg2, as gpg2 insists on
# prompting for passwords
for
s
in
(
'gpg'
,
'gpg2'
):
try
:
rc
=
subprocess
.
check_call
([
s
,
'--version'
],
stdout
=
sink
,
stderr
=
sink
)
if
rc
==
0
:
self
.
gpg
=
s
break
except
OSError
:
pass
def
_get_pypirc_command
(
self
):
"""
Get the distutils command for interacting with PyPI configurations.
:return: the command.
"""
from
distutils.core
import
Distribution
from
distutils.config
import
PyPIRCCommand
d
=
Distribution
()
return
PyPIRCCommand
(
d
)
def
read_configuration
(
self
):
"""
Read the PyPI access configuration as supported by distutils, getting
PyPI to do the actual work. This populates ``username``, ``password``,
``realm`` and ``url`` attributes from the configuration.
"""
# get distutils to do the work
c
=
self
.
_get_pypirc_command
()
c
.
repository
=
self
.
url
cfg
=
c
.
_read_pypirc
()
self
.
username
=
cfg
.
get
(
'username'
)
self
.
password
=
cfg
.
get
(
'password'
)
self
.
realm
=
cfg
.
get
(
'realm'
,
'pypi'
)
self
.
url
=
cfg
.
get
(
'repository'
,
self
.
url
)
def
save_configuration
(
self
):
"""
Save the PyPI access configuration. You must have set ``username`` and
``password`` attributes before calling this method.
Again, distutils is used to do the actual work.
"""
self
.
check_credentials
()
# get distutils to do the work
c
=
self
.
_get_pypirc_command
()
c
.
_store_pypirc
(
self
.
username
,
self
.
password
)
def
check_credentials
(
self
):
"""
Check that ``username`` and ``password`` have been set, and raise an
exception if not.
"""
if
self
.
username
is
None
or
self
.
password
is
None
:
raise
DistlibException
(
'username and password must be set'
)
pm
=
HTTPPasswordMgr
()
_
,
netloc
,
_
,
_
,
_
,
_
=
urlparse
(
self
.
url
)
pm
.
add_password
(
self
.
realm
,
netloc
,
self
.
username
,
self
.
password
)
self
.
password_handler
=
HTTPBasicAuthHandler
(
pm
)
def
register
(
self
,
metadata
):
"""
Register a distribution on PyPI, using the provided metadata.
:param metadata: A :class:`Metadata` instance defining at least a name
and version number for the distribution to be
registered.
:return: The HTTP response received from PyPI upon submission of the
request.
"""
self
.
check_credentials
()
metadata
.
validate
()
d
=
metadata
.
todict
()
d
[
':action'
]
=
'verify'
request
=
self
.
encode_request
(
d
.
items
(),
[])
response
=
self
.
send_request
(
request
)
d
[
':action'
]
=
'submit'
request
=
self
.
encode_request
(
d
.
items
(),
[])
return
self
.
send_request
(
request
)
def
_reader
(
self
,
name
,
stream
,
outbuf
):
"""
Thread runner for reading lines of from a subprocess into a buffer.
:param name: The logical name of the stream (used for logging only).
:param stream: The stream to read from. This will typically a pipe
connected to the output stream of a subprocess.
:param outbuf: The list to append the read lines to.
"""
while
True
:
s
=
stream
.
readline
()
if
not
s
:
break
s
=
s
.
decode
(
'utf-8'
)
.
rstrip
()
outbuf
.
append
(
s
)
logger
.
debug
(
'
%
s:
%
s'
%
(
name
,
s
))
stream
.
close
()
def
get_sign_command
(
self
,
filename
,
signer
,
sign_password
,
keystore
=
None
):
"""
Return a suitable command for signing a file.
:param filename: The pathname to the file to be signed.
:param signer: The identifier of the signer of the file.
:param sign_password: The passphrase for the signer's
private key used for signing.
:param keystore: The path to a directory which contains the keys
used in verification. If not specified, the
instance's ``gpg_home`` attribute is used instead.
:return: The signing command as a list suitable to be
passed to :class:`subprocess.Popen`.
"""
cmd
=
[
self
.
gpg
,
'--status-fd'
,
'2'
,
'--no-tty'
]
if
keystore
is
None
:
keystore
=
self
.
gpg_home
if
keystore
:
cmd
.
extend
([
'--homedir'
,
keystore
])
if
sign_password
is
not
None
:
cmd
.
extend
([
'--batch'
,
'--passphrase-fd'
,
'0'
])
td
=
tempfile
.
mkdtemp
()
sf
=
os
.
path
.
join
(
td
,
os
.
path
.
basename
(
filename
)
+
'.asc'
)
cmd
.
extend
([
'--detach-sign'
,
'--armor'
,
'--local-user'
,
signer
,
'--output'
,
sf
,
filename
])
logger
.
debug
(
'invoking:
%
s'
,
' '
.
join
(
cmd
))
return
cmd
,
sf
def
run_command
(
self
,
cmd
,
input_data
=
None
):
"""
Run a command in a child process , passing it any input data specified.
:param cmd: The command to run.
:param input_data: If specified, this must be a byte string containing
data to be sent to the child process.
:return: A tuple consisting of the subprocess' exit code, a list of
lines read from the subprocess' ``stdout``, and a list of
lines read from the subprocess' ``stderr``.
"""
kwargs
=
{
'stdout'
:
subprocess
.
PIPE
,
'stderr'
:
subprocess
.
PIPE
,
}
if
input_data
is
not
None
:
kwargs
[
'stdin'
]
=
subprocess
.
PIPE
stdout
=
[]
stderr
=
[]
p
=
subprocess
.
Popen
(
cmd
,
**
kwargs
)
# We don't use communicate() here because we may need to
# get clever with interacting with the command
t1
=
Thread
(
target
=
self
.
_reader
,
args
=
(
'stdout'
,
p
.
stdout
,
stdout
))
t1
.
start
()
t2
=
Thread
(
target
=
self
.
_reader
,
args
=
(
'stderr'
,
p
.
stderr
,
stderr
))
t2
.
start
()
if
input_data
is
not
None
:
p
.
stdin
.
write
(
input_data
)
p
.
stdin
.
close
()
p
.
wait
()
t1
.
join
()
t2
.
join
()
return
p
.
returncode
,
stdout
,
stderr
def
sign_file
(
self
,
filename
,
signer
,
sign_password
,
keystore
=
None
):
"""
Sign a file.
:param filename: The pathname to the file to be signed.
:param signer: The identifier of the signer of the file.
:param sign_password: The passphrase for the signer's
private key used for signing.
:param keystore: The path to a directory which contains the keys
used in signing. If not specified, the instance's
``gpg_home`` attribute is used instead.
:return: The absolute pathname of the file where the signature is
stored.
"""
cmd
,
sig_file
=
self
.
get_sign_command
(
filename
,
signer
,
sign_password
,
keystore
)
rc
,
stdout
,
stderr
=
self
.
run_command
(
cmd
,
sign_password
.
encode
(
'utf-8'
))
if
rc
!=
0
:
raise
DistlibException
(
'sign command failed with error '
'code
%
s'
%
rc
)
return
sig_file
def
upload_file
(
self
,
metadata
,
filename
,
signer
=
None
,
sign_password
=
None
,
filetype
=
'sdist'
,
pyversion
=
'source'
,
keystore
=
None
):
"""
Upload a release file to the index.
:param metadata: A :class:`Metadata` instance defining at least a name
and version number for the file to be uploaded.
:param filename: The pathname of the file to be uploaded.
:param signer: The identifier of the signer of the file.
:param sign_password: The passphrase for the signer's
private key used for signing.
:param filetype: The type of the file being uploaded. This is the
distutils command which produced that file, e.g.
``sdist`` or ``bdist_wheel``.
:param pyversion: The version of Python which the release relates
to. For code compatible with any Python, this would
be ``source``, otherwise it would be e.g. ``3.2``.
:param keystore: The path to a directory which contains the keys
used in signing. If not specified, the instance's
``gpg_home`` attribute is used instead.
:return: The HTTP response received from PyPI upon submission of the
request.
"""
self
.
check_credentials
()
if
not
os
.
path
.
exists
(
filename
):
raise
DistlibException
(
'not found:
%
s'
%
filename
)
metadata
.
validate
()
d
=
metadata
.
todict
()
sig_file
=
None
if
signer
:
if
not
self
.
gpg
:
logger
.
warning
(
'no signing program available - not signed'
)
else
:
sig_file
=
self
.
sign_file
(
filename
,
signer
,
sign_password
,
keystore
)
with
open
(
filename
,
'rb'
)
as
f
:
file_data
=
f
.
read
()
md5_digest
=
hashlib
.
md5
(
file_data
)
.
hexdigest
()
sha256_digest
=
hashlib
.
sha256
(
file_data
)
.
hexdigest
()
d
.
update
({
':action'
:
'file_upload'
,
'protocol_version'
:
'1'
,
'filetype'
:
filetype
,
'pyversion'
:
pyversion
,
'md5_digest'
:
md5_digest
,
'sha256_digest'
:
sha256_digest
,
})
files
=
[(
'content'
,
os
.
path
.
basename
(
filename
),
file_data
)]
if
sig_file
:
with
open
(
sig_file
,
'rb'
)
as
f
:
sig_data
=
f
.
read
()
files
.
append
((
'gpg_signature'
,
os
.
path
.
basename
(
sig_file
),
sig_data
))
shutil
.
rmtree
(
os
.
path
.
dirname
(
sig_file
))
request
=
self
.
encode_request
(
d
.
items
(),
files
)
return
self
.
send_request
(
request
)
def
upload_documentation
(
self
,
metadata
,
doc_dir
):
"""
Upload documentation to the index.
:param metadata: A :class:`Metadata` instance defining at least a name
and version number for the documentation to be
uploaded.
:param doc_dir: The pathname of the directory which contains the
documentation. This should be the directory that
contains the ``index.html`` for the documentation.
:return: The HTTP response received from PyPI upon submission of the
request.
"""
self
.
check_credentials
()
if
not
os
.
path
.
isdir
(
doc_dir
):
raise
DistlibException
(
'not a directory:
%
r'
%
doc_dir
)
fn
=
os
.
path
.
join
(
doc_dir
,
'index.html'
)
if
not
os
.
path
.
exists
(
fn
):
raise
DistlibException
(
'not found:
%
r'
%
fn
)
metadata
.
validate
()
name
,
version
=
metadata
.
name
,
metadata
.
version
zip_data
=
zip_dir
(
doc_dir
)
.
getvalue
()
fields
=
[(
':action'
,
'doc_upload'
),
(
'name'
,
name
),
(
'version'
,
version
)]
files
=
[(
'content'
,
name
,
zip_data
)]
request
=
self
.
encode_request
(
fields
,
files
)
return
self
.
send_request
(
request
)
def
get_verify_command
(
self
,
signature_filename
,
data_filename
,
keystore
=
None
):
"""
Return a suitable command for verifying a file.
:param signature_filename: The pathname to the file containing the
signature.
:param data_filename: The pathname to the file containing the
signed data.
:param keystore: The path to a directory which contains the keys
used in verification. If not specified, the
instance's ``gpg_home`` attribute is used instead.
:return: The verifying command as a list suitable to be
passed to :class:`subprocess.Popen`.
"""
cmd
=
[
self
.
gpg
,
'--status-fd'
,
'2'
,
'--no-tty'
]
if
keystore
is
None
:
keystore
=
self
.
gpg_home
if
keystore
:
cmd
.
extend
([
'--homedir'
,
keystore
])
cmd
.
extend
([
'--verify'
,
signature_filename
,
data_filename
])
logger
.
debug
(
'invoking:
%
s'
,
' '
.
join
(
cmd
))
return
cmd
def
verify_signature
(
self
,
signature_filename
,
data_filename
,
keystore
=
None
):
"""
Verify a signature for a file.
:param signature_filename: The pathname to the file containing the
signature.
:param data_filename: The pathname to the file containing the
signed data.
:param keystore: The path to a directory which contains the keys
used in verification. If not specified, the
instance's ``gpg_home`` attribute is used instead.
:return: True if the signature was verified, else False.
"""
if
not
self
.
gpg
:
raise
DistlibException
(
'verification unavailable because gpg '
'unavailable'
)
cmd
=
self
.
get_verify_command
(
signature_filename
,
data_filename
,
keystore
)
rc
,
stdout
,
stderr
=
self
.
run_command
(
cmd
)
if
rc
not
in
(
0
,
1
):
raise
DistlibException
(
'verify command failed with error '
'code
%
s'
%
rc
)
return
rc
==
0
def
download_file
(
self
,
url
,
destfile
,
digest
=
None
,
reporthook
=
None
):
"""
This is a convenience method for downloading a file from an URL.
Normally, this will be a file from the index, though currently
no check is made for this (i.e. a file can be downloaded from
anywhere).
The method is just like the :func:`urlretrieve` function in the
standard library, except that it allows digest computation to be
done during download and checking that the downloaded data
matched any expected value.
:param url: The URL of the file to be downloaded (assumed to be
available via an HTTP GET request).
:param destfile: The pathname where the downloaded file is to be
saved.
:param digest: If specified, this must be a (hasher, value)
tuple, where hasher is the algorithm used (e.g.
``'md5'``) and ``value`` is the expected value.
:param reporthook: The same as for :func:`urlretrieve` in the
standard library.
"""
if
digest
is
None
:
digester
=
None
logger
.
debug
(
'No digest specified'
)
else
:
if
isinstance
(
digest
,
(
list
,
tuple
)):
hasher
,
digest
=
digest
else
:
hasher
=
'md5'
digester
=
getattr
(
hashlib
,
hasher
)()
logger
.
debug
(
'Digest specified:
%
s'
%
digest
)
# The following code is equivalent to urlretrieve.
# We need to do it this way so that we can compute the
# digest of the file as we go.
with
open
(
destfile
,
'wb'
)
as
dfp
:
# addinfourl is not a context manager on 2.x
# so we have to use try/finally
sfp
=
self
.
send_request
(
Request
(
url
))
try
:
headers
=
sfp
.
info
()
blocksize
=
8192
size
=
-
1
read
=
0
blocknum
=
0
if
"content-length"
in
headers
:
size
=
int
(
headers
[
"Content-Length"
])
if
reporthook
:
reporthook
(
blocknum
,
blocksize
,
size
)
while
True
:
block
=
sfp
.
read
(
blocksize
)
if
not
block
:
break
read
+=
len
(
block
)
dfp
.
write
(
block
)
if
digester
:
digester
.
update
(
block
)
blocknum
+=
1
if
reporthook
:
reporthook
(
blocknum
,
blocksize
,
size
)
finally
:
sfp
.
close
()
# check that we got the whole file, if we can
if
size
>=
0
and
read
<
size
:
raise
DistlibException
(
'retrieval incomplete: got only
%
d out of
%
d bytes'
%
(
read
,
size
))
# if we have a digest, it must match.
if
digester
:
actual
=
digester
.
hexdigest
()
if
digest
!=
actual
:
raise
DistlibException
(
'
%
s digest mismatch for
%
s: expected '
'
%
s, got
%
s'
%
(
hasher
,
destfile
,
digest
,
actual
))
logger
.
debug
(
'Digest verified:
%
s'
,
digest
)
def
send_request
(
self
,
req
):
"""
Send a standard library :class:`Request` to PyPI and return its
response.
:param req: The request to send.
:return: The HTTP response from PyPI (a standard library HTTPResponse).
"""
handlers
=
[]
if
self
.
password_handler
:
handlers
.
append
(
self
.
password_handler
)
if
self
.
ssl_verifier
:
handlers
.
append
(
self
.
ssl_verifier
)
opener
=
build_opener
(
*
handlers
)
return
opener
.
open
(
req
)
def
encode_request
(
self
,
fields
,
files
):
"""
Encode fields and files for posting to an HTTP server.
:param fields: The fields to send as a list of (fieldname, value)
tuples.
:param files: The files to send as a list of (fieldname, filename,
file_bytes) tuple.
"""
# Adapted from packaging, which in turn was adapted from
# http://code.activestate.com/recipes/146306
parts
=
[]
boundary
=
self
.
boundary
for
k
,
values
in
fields
:
if
not
isinstance
(
values
,
(
list
,
tuple
)):
values
=
[
values
]
for
v
in
values
:
parts
.
extend
((
b
'--'
+
boundary
,
(
'Content-Disposition: form-data; name="
%
s"'
%
k
)
.
encode
(
'utf-8'
),
b
''
,
v
.
encode
(
'utf-8'
)))
for
key
,
filename
,
value
in
files
:
parts
.
extend
((
b
'--'
+
boundary
,
(
'Content-Disposition: form-data; name="
%
s"; filename="
%
s"'
%
(
key
,
filename
))
.
encode
(
'utf-8'
),
b
''
,
value
))
parts
.
extend
((
b
'--'
+
boundary
+
b
'--'
,
b
''
))
body
=
b
'
\r\n
'
.
join
(
parts
)
ct
=
b
'multipart/form-data; boundary='
+
boundary
headers
=
{
'Content-type'
:
ct
,
'Content-length'
:
str
(
len
(
body
))
}
return
Request
(
self
.
url
,
body
,
headers
)
def
search
(
self
,
terms
,
operator
=
None
):
if
isinstance
(
terms
,
string_types
):
terms
=
{
'name'
:
terms
}
rpc_proxy
=
ServerProxy
(
self
.
url
,
timeout
=
3.0
)
try
:
return
rpc_proxy
.
search
(
terms
,
operator
or
'and'
)
finally
:
rpc_proxy
(
'close'
)()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment