Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install build dependencies as specified in PEP 518 #4144

Merged
merged 43 commits into from
May 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4f0fd08
Vendor pytoml==0.1.11
takluyver Nov 25, 2016
c24f432
Add property to locate pyproject_toml in downloaded source package
takluyver Nov 25, 2016
779bbaa
Temporary prefix for installing build dependencies
takluyver Nov 29, 2016
b31c87f
Install build requirements for projects using PEP 518
takluyver Nov 29, 2016
92bfdf7
Isolate wheel builds when using PEP 518 build dependencies
takluyver Nov 29, 2016
3da2cef
Hopefully satisfy pep8
takluyver Nov 29, 2016
32eebbf
Compatibility fallback for absent sysconfig module on Python 2.6
takluyver Nov 29, 2016
191d680
Let's see if that keeps PEP8 happy
takluyver Dec 2, 2016
b4f65bf
Try adding smoketest of pep518 build dependency installation
takluyver Dec 2, 2016
f257e85
Try to fix pep518 test
takluyver Dec 2, 2016
397066b
Correct install command for build deps
takluyver Dec 9, 2016
8a9d049
Use finder to find build deps to install
takluyver Dec 9, 2016
3a68e97
Wrap each build req in an InstallRequirement
takluyver Dec 19, 2016
d0a9b4b
Make import local to avoid circular import
takluyver Dec 19, 2016
b170957
Disable isolation so packages can use setuptools
takluyver Dec 19, 2016
ea108f8
Use pip.__main__ to invoke pip on Python 2.6
takluyver Dec 19, 2016
653e291
Add source of sample distribution for tests
takluyver Jan 18, 2017
cfbc4d9
Use no_clean option for wheel build environment
takluyver Jan 18, 2017
a8cdd4a
Fix thinko
takluyver Jan 26, 2017
094b441
Python 2.6 is no longer supported :-)
takluyver Apr 1, 2017
b0b6ec1
Add news file
takluyver Apr 1, 2017
a50c4ef
Fix code style
takluyver Apr 1, 2017
c9d83b9
Let context exit implicitly return None, as requested
takluyver Apr 12, 2017
618720d
build-system.requires = [] -> ["setuptools", "wheel"]
takluyver Apr 12, 2017
9a79fb5
Allow build-system requirements to be empty
takluyver Apr 12, 2017
7561bf2
Re-disable isolation for wheel builds
takluyver Apr 13, 2017
99e00fb
Add package of 'wheel' in test packages directory
takluyver Apr 13, 2017
034efe5
Let pip look in packages directory for 'wheel -e' test
takluyver Apr 13, 2017
a9c4081
Warning message if package doesn't build-dep on setuptools
takluyver Apr 13, 2017
b1d0e6b
Avoid uninstalling setuptools when installing build dependencies
takluyver Apr 13, 2017
c9df9a7
Include newer setuptools for tests
takluyver Apr 13, 2017
65585b9
Allow temp files in test of --no-clean option
takluyver Apr 26, 2017
1164677
Merge branch 'pep518' of github.com:takluyver/pip into pep518
takluyver Apr 26, 2017
3adc1f8
Ignore --no-binary when installing build-system
takluyver Apr 26, 2017
fee06aa
Fix indentation
takluyver Apr 26, 2017
1672f82
Update vendored pytoml to 0.1.12
takluyver May 13, 2017
2b2bdd1
Download setuptools, wheel during tests
takluyver May 13, 2017
aee5b6c
Download packages to path, not to file:/// URL
takluyver May 13, 2017
2e86516
Find build requirements in the tmp copy of test data folder
takluyver May 13, 2017
61f89c5
Make pep8 happy again
takluyver May 13, 2017
9c7ed29
Fix some more tests which build packages from source
takluyver May 13, 2017
2095581
Merge branch 'master' into pep518
takluyver May 19, 2017
15ac808
Fix duplicate imports and code style
takluyver May 19, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions news/3691.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Support for packages specifying build dependencies in pyproject.toml (see `PEP
518 <https://www.python.org/dev/peps/pep-0518/>`__). Packages which specify
one or more build dependencies this way will be built into wheels in an
isolated environment with those dependencies installed.
3 changes: 3 additions & 0 deletions pip/_vendor/pytoml/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .core import TomlError
from .parser import load, loads
from .writer import dump, dumps
13 changes: 13 additions & 0 deletions pip/_vendor/pytoml/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class TomlError(RuntimeError):
def __init__(self, message, line, col, filename):
RuntimeError.__init__(self, message, line, col, filename)
self.message = message
self.line = line
self.col = col
self.filename = filename

def __str__(self):
return '{}({}, {}): {}'.format(self.filename, self.line, self.col, self.message)

def __repr__(self):
return 'TomlError({!r}, {!r}, {!r}, {!r})'.format(self.message, self.line, self.col, self.filename)
366 changes: 366 additions & 0 deletions pip/_vendor/pytoml/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,366 @@
import string, re, sys, datetime
from .core import TomlError

if sys.version_info[0] == 2:
_chr = unichr
else:
_chr = chr

def load(fin, translate=lambda t, x, v: v):
return loads(fin.read(), translate=translate, filename=getattr(fin, 'name', repr(fin)))

def loads(s, filename='<string>', translate=lambda t, x, v: v):
if isinstance(s, bytes):
s = s.decode('utf-8')

s = s.replace('\r\n', '\n')

root = {}
tables = {}
scope = root

src = _Source(s, filename=filename)
ast = _p_toml(src)

def error(msg):
raise TomlError(msg, pos[0], pos[1], filename)

def process_value(v):
kind, text, value, pos = v
if kind == 'str' and value.startswith('\n'):
value = value[1:]
if kind == 'array':
if value and any(k != value[0][0] for k, t, v, p in value[1:]):
error('array-type-mismatch')
value = [process_value(item) for item in value]
elif kind == 'table':
value = dict([(k, process_value(value[k])) for k in value])
return translate(kind, text, value)

for kind, value, pos in ast:
if kind == 'kv':
k, v = value
if k in scope:
error('duplicate_keys. Key "{0}" was used more than once.'.format(k))
scope[k] = process_value(v)
else:
is_table_array = (kind == 'table_array')
cur = tables
for name in value[:-1]:
if isinstance(cur.get(name), list):
d, cur = cur[name][-1]
else:
d, cur = cur.setdefault(name, (None, {}))

scope = {}
name = value[-1]
if name not in cur:
if is_table_array:
cur[name] = [(scope, {})]
else:
cur[name] = (scope, {})
elif isinstance(cur[name], list):
if not is_table_array:
error('table_type_mismatch')
cur[name].append((scope, {}))
else:
if is_table_array:
error('table_type_mismatch')
old_scope, next_table = cur[name]
if old_scope is not None:
error('duplicate_tables')
cur[name] = (scope, next_table)

def merge_tables(scope, tables):
if scope is None:
scope = {}
for k in tables:
if k in scope:
error('key_table_conflict')
v = tables[k]
if isinstance(v, list):
scope[k] = [merge_tables(sc, tbl) for sc, tbl in v]
else:
scope[k] = merge_tables(v[0], v[1])
return scope

return merge_tables(root, tables)

class _Source:
def __init__(self, s, filename=None):
self.s = s
self._pos = (1, 1)
self._last = None
self._filename = filename
self.backtrack_stack = []

def last(self):
return self._last

def pos(self):
return self._pos

def fail(self):
return self._expect(None)

def consume_dot(self):
if self.s:
self._last = self.s[0]
self.s = self[1:]
self._advance(self._last)
return self._last
return None

def expect_dot(self):
return self._expect(self.consume_dot())

def consume_eof(self):
if not self.s:
self._last = ''
return True
return False

def expect_eof(self):
return self._expect(self.consume_eof())

def consume(self, s):
if self.s.startswith(s):
self.s = self.s[len(s):]
self._last = s
self._advance(s)
return True
return False

def expect(self, s):
return self._expect(self.consume(s))

def consume_re(self, re):
m = re.match(self.s)
if m:
self.s = self.s[len(m.group(0)):]
self._last = m
self._advance(m.group(0))
return m
return None

def expect_re(self, re):
return self._expect(self.consume_re(re))

def __enter__(self):
self.backtrack_stack.append((self.s, self._pos))

def __exit__(self, type, value, traceback):
if type is None:
self.backtrack_stack.pop()
else:
self.s, self._pos = self.backtrack_stack.pop()
return type == TomlError

def commit(self):
self.backtrack_stack[-1] = (self.s, self._pos)

def _expect(self, r):
if not r:
raise TomlError('msg', self._pos[0], self._pos[1], self._filename)
return r

def _advance(self, s):
suffix_pos = s.rfind('\n')
if suffix_pos == -1:
self._pos = (self._pos[0], self._pos[1] + len(s))
else:
self._pos = (self._pos[0] + s.count('\n'), len(s) - suffix_pos)

_ews_re = re.compile(r'(?:[ \t]|#[^\n]*\n|#[^\n]*\Z|\n)*')
def _p_ews(s):
s.expect_re(_ews_re)

_ws_re = re.compile(r'[ \t]*')
def _p_ws(s):
s.expect_re(_ws_re)

_escapes = { 'b': '\b', 'n': '\n', 'r': '\r', 't': '\t', '"': '"', '\'': '\'',
'\\': '\\', '/': '/', 'f': '\f' }

_basicstr_re = re.compile(r'[^"\\\000-\037]*')
_short_uni_re = re.compile(r'u([0-9a-fA-F]{4})')
_long_uni_re = re.compile(r'U([0-9a-fA-F]{8})')
_escapes_re = re.compile('[bnrt"\'\\\\/f]')
_newline_esc_re = re.compile('\n[ \t\n]*')
def _p_basicstr_content(s, content=_basicstr_re):
res = []
while True:
res.append(s.expect_re(content).group(0))
if not s.consume('\\'):
break
if s.consume_re(_newline_esc_re):
pass
elif s.consume_re(_short_uni_re) or s.consume_re(_long_uni_re):
res.append(_chr(int(s.last().group(1), 16)))
else:
s.expect_re(_escapes_re)
res.append(_escapes[s.last().group(0)])
return ''.join(res)

_key_re = re.compile(r'[0-9a-zA-Z-_]+')
def _p_key(s):
with s:
s.expect('"')
r = _p_basicstr_content(s, _basicstr_re)
s.expect('"')
return r
return s.expect_re(_key_re).group(0)

_float_re = re.compile(r'[+-]?(?:0|[1-9](?:_?\d)*)(?:\.\d(?:_?\d)*)?(?:[eE][+-]?(?:\d(?:_?\d)*))?')
_datetime_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))')

_basicstr_ml_re = re.compile(r'(?:(?:|"|"")[^"\\\000-\011\013-\037])*')
_litstr_re = re.compile(r"[^'\000-\037]*")
_litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\011\013-\037]))*")
def _p_value(s):
pos = s.pos()

if s.consume('true'):
return 'bool', s.last(), True, pos
if s.consume('false'):
return 'bool', s.last(), False, pos

if s.consume('"'):
if s.consume('""'):
r = _p_basicstr_content(s, _basicstr_ml_re)
s.expect('"""')
else:
r = _p_basicstr_content(s, _basicstr_re)
s.expect('"')
return 'str', r, r, pos

if s.consume('\''):
if s.consume('\'\''):
r = s.expect_re(_litstr_ml_re).group(0)
s.expect('\'\'\'')
else:
r = s.expect_re(_litstr_re).group(0)
s.expect('\'')
return 'str', r, r, pos

if s.consume_re(_datetime_re):
m = s.last()
s0 = m.group(0)
r = map(int, m.groups()[:6])
if m.group(7):
micro = float(m.group(7))
else:
micro = 0

if m.group(8):
g = int(m.group(8), 10) * 60 + int(m.group(9), 10)
tz = _TimeZone(datetime.timedelta(0, g * 60))
else:
tz = _TimeZone(datetime.timedelta(0, 0))

y, m, d, H, M, S = r
dt = datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz)
return 'datetime', s0, dt, pos

if s.consume_re(_float_re):
m = s.last().group(0)
r = m.replace('_','')
if '.' in m or 'e' in m or 'E' in m:
return 'float', m, float(r), pos
else:
return 'int', m, int(r, 10), pos

if s.consume('['):
items = []
with s:
while True:
_p_ews(s)
items.append(_p_value(s))
s.commit()
_p_ews(s)
s.expect(',')
s.commit()
_p_ews(s)
s.expect(']')
return 'array', None, items, pos

if s.consume('{'):
_p_ws(s)
items = {}
if not s.consume('}'):
k = _p_key(s)
_p_ws(s)
s.expect('=')
_p_ws(s)
items[k] = _p_value(s)
_p_ws(s)
while s.consume(','):
_p_ws(s)
k = _p_key(s)
_p_ws(s)
s.expect('=')
_p_ws(s)
items[k] = _p_value(s)
_p_ws(s)
s.expect('}')
return 'table', None, items, pos

s.fail()

def _p_stmt(s):
pos = s.pos()
if s.consume( '['):
is_array = s.consume('[')
_p_ws(s)
keys = [_p_key(s)]
_p_ws(s)
while s.consume('.'):
_p_ws(s)
keys.append(_p_key(s))
_p_ws(s)
s.expect(']')
if is_array:
s.expect(']')
return 'table_array' if is_array else 'table', keys, pos

key = _p_key(s)
_p_ws(s)
s.expect('=')
_p_ws(s)
value = _p_value(s)
return 'kv', (key, value), pos

_stmtsep_re = re.compile(r'(?:[ \t]*(?:#[^\n]*)?\n)+[ \t]*')
def _p_toml(s):
stmts = []
_p_ews(s)
with s:
stmts.append(_p_stmt(s))
while True:
s.commit()
s.expect_re(_stmtsep_re)
stmts.append(_p_stmt(s))
_p_ews(s)
s.expect_eof()
return stmts

class _TimeZone(datetime.tzinfo):
def __init__(self, offset):
self._offset = offset

def utcoffset(self, dt):
return self._offset

def dst(self, dt):
return None

def tzname(self, dt):
m = self._offset.total_seconds() // 60
if m < 0:
res = '-'
m = -m
else:
res = '+'
h = m // 60
m = m - h * 60
return '{}{:.02}{:.02}'.format(res, h, m)
Loading