Skip to content

Commit 789514b

Browse files
committed
Merge branch 'master' into mypy
* master: [requires.io] dependency update [requires.io] dependency update [requires.io] dependency update [requires.io] dependency update update decription Add docs-auto Tox environment add some tests and a fix Revert "Work around flake8 bug" Work around flake8 bug Un-export parse_host and remove it from the docs. Forgot to pin sphinx-rtd-theme parse is exported publicly but not documented. parse_host is documented but not exported publicly. URL.family does not exist. Add docs environment to tox config. # Conflicts: # src/hyperlink/_url.py
2 parents 387af6f + ba695fb commit 789514b

6 files changed

Lines changed: 130 additions & 41 deletions

File tree

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ matrix:
2626
env: TOXENV=test-pypy3,codecov
2727
- python: "2.7"
2828
env: TOXENV=packaging-py27
29+
- python: "3.8"
30+
env: TOXENV=docs
2931

3032

3133
install:

docs/api.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ URLs have many parts, and URL objects have many attributes to represent them.
6161
.. autoattribute:: hyperlink.URL.userinfo
6262
.. autoattribute:: hyperlink.URL.user
6363
.. autoattribute:: hyperlink.URL.rooted
64-
.. autoattribute:: hyperlink.URL.family
6564

6665
Low-level functions
6766
-------------------
@@ -70,6 +69,6 @@ A couple of notable helpers used by the :class:`~hyperlink.URL` type.
7069

7170
.. autoclass:: hyperlink.URLParseError
7271
.. autofunction:: hyperlink.register_scheme
73-
.. autofunction:: hyperlink.parse_host
72+
.. autofunction:: hyperlink.parse
7473

7574
.. TODO: run doctests in docs?

src/hyperlink/__init__.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
from ._url import (
2+
parse,
3+
register_scheme,
4+
URL,
5+
EncodedURL,
6+
DecodedURL,
7+
URLParseError,
8+
)
19

2-
from ._url import (URL,
3-
parse,
4-
EncodedURL,
5-
DecodedURL,
6-
URLParseError,
7-
register_scheme)
8-
9-
__all__ = [
10-
"URL",
10+
__all__ = (
1111
"parse",
12+
"register_scheme",
13+
"URL",
1214
"EncodedURL",
1315
"DecodedURL",
1416
"URLParseError",
15-
"register_scheme",
16-
]
17+
)

src/hyperlink/_url.py

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -797,25 +797,29 @@ class URL(object):
797797
constructor arguments is below.
798798
799799
Args:
800-
scheme: The text name of the scheme.
801-
host: The host portion of the network location
802-
port: The port part of the network location. If ``None`` or no port is
803-
passed, the port will default to the default port of the scheme, if
804-
it is known. See the ``SCHEME_PORT_MAP`` and
805-
:func:`register_default_port` for more info.
806-
path: A tuple of strings representing the slash-separated parts of the
807-
path.
808-
query: The query parameters, as a dictionary or as an iterable of
809-
key-value pairs.
810-
fragment: The fragment part of the URL.
811-
rooted: Whether or not the path begins with a slash.
812-
userinfo: The username or colon-separated username:password pair.
813-
uses_netloc: Indicates whether two slashes appear between the scheme
814-
and the host (``http://eg.com`` vs. ``mailto:e@g.com``).
815-
Set automatically based on scheme.
816-
817-
All of these parts are also exposed as read-only attributes of URL
818-
instances, along with several useful methods.
800+
scheme: The text name of the scheme.
801+
host: The host portion of the network location
802+
port: The port part of the network location. If
803+
``None`` or no port is passed, the port will default to
804+
the default port of the scheme, if it is known. See the
805+
``SCHEME_PORT_MAP`` and :func:`register_default_port`
806+
for more info.
807+
path: A tuple of strings representing the
808+
slash-separated parts of the path.
809+
query: The query parameters, as a dictionary or
810+
as an iterable of key-value pairs.
811+
fragment: The fragment part of the URL.
812+
rooted: A rooted URL is one which indicates an absolute path.
813+
This is True on any URL that includes a host, or any relative URL
814+
that starts with a slash.
815+
userinfo: The username or colon-separated
816+
username:password pair.
817+
uses_netloc: Indicates whether two slashes appear
818+
between the scheme and the host (``http://eg.com`` vs
819+
``mailto:e@g.com``). Set automatically based on scheme.
820+
821+
All of these parts are also exposed as read-only attributes of
822+
URL instances, along with several useful methods.
819823
820824
.. _RFC 3986: https://tools.ietf.org/html/rfc3986
821825
.. _RFC 3987: https://tools.ietf.org/html/rfc3987
@@ -880,8 +884,12 @@ def __init__(
880884
uses_netloc = scheme_uses_netloc(self._scheme, uses_netloc)
881885
self._uses_netloc = _typecheck("uses_netloc",
882886
uses_netloc, bool, NoneType)
883-
884-
return
887+
# fixup for rooted consistency
888+
if self._host:
889+
self._rooted = True
890+
if (not self._rooted) and self._path and self._path[0] == '':
891+
self._rooted = True
892+
self._path = self._path[1:]
885893

886894
def get_decoded_url(self, lazy=False):
887895
# type: (bool) -> DecodedURL
@@ -1051,7 +1059,7 @@ def __eq__(self, other):
10511059
if not isinstance(other, self.__class__):
10521060
return NotImplemented
10531061
for attr in ['scheme', 'userinfo', 'host', 'query',
1054-
'fragment', 'port', 'uses_netloc']:
1062+
'fragment', 'port', 'uses_netloc', 'rooted']:
10551063
if getattr(self, attr) != getattr(other, attr):
10561064
return False
10571065
if (

src/hyperlink/test/test_url.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,42 @@ def test_netloc_slashes(self):
10811081

10821082
return
10831083

1084+
def test_rooted_to_relative(self):
1085+
# type: () -> None
1086+
"""
1087+
On host-relative URLs, the C{rooted} flag can be updated to indicate
1088+
that the path should no longer be treated as absolute.
1089+
"""
1090+
a = URL(path=['hello'])
1091+
self.assertEqual(a.to_text(), 'hello')
1092+
b = a.replace(rooted=True)
1093+
self.assertEqual(b.to_text(), '/hello')
1094+
self.assertNotEqual(a, b)
1095+
1096+
def test_autorooted(self):
1097+
# type: () -> None
1098+
"""
1099+
The C{rooted} flag can be updated in some cases, but it cannot be made
1100+
to conflict with other facts surrounding the URL; for example, all URLs
1101+
involving an authority (host) are inherently rooted because it is not
1102+
syntactically possible to express otherwise; also, once an unrooted URL
1103+
gains a path that starts with an empty string, that empty string is
1104+
elided and it becomes rooted, because these cases are syntactically
1105+
indistinguisable in real URL text.
1106+
"""
1107+
relative_path_rooted = URL(path=['', 'foo'], rooted=False)
1108+
self.assertEqual(relative_path_rooted.rooted, True)
1109+
relative_flag_rooted = URL(path=['foo'], rooted=True)
1110+
self.assertEqual(relative_flag_rooted.rooted, True)
1111+
self.assertEqual(relative_path_rooted, relative_flag_rooted)
1112+
1113+
attempt_unrooted_absolute = URL(host="foo", path=['bar'], rooted=False)
1114+
normal_absolute = URL(host="foo", path=["bar"])
1115+
attempted_rooted_replacement = normal_absolute.replace(rooted=True)
1116+
self.assertEqual(attempt_unrooted_absolute, normal_absolute)
1117+
self.assertEqual(normal_absolute.rooted, True)
1118+
self.assertEqual(attempt_unrooted_absolute.rooted, True)
1119+
10841120
def test_wrong_constructor(self):
10851121
# type: () -> None
10861122
with self.assertRaises(ValueError):

tox.ini

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ envlist =
55
test-py{26,27,34,35,36,37,38,py,py3}
66
coverage_report
77
packaging
8+
docs
89

910
skip_missing_interpreters = {tty:True:False}
1011

@@ -143,7 +144,7 @@ skip_install = True
143144

144145

145146
deps =
146-
mypy==0.740
147+
mypy==0.750
147148

148149

149150
commands =
@@ -218,11 +219,53 @@ deps =
218219
commands =
219220
coverage combine
220221
coverage xml -o "{env:COVERAGE_XML}"
221-
codecov --file="{env:COVERAGE_XML}" --env \
222-
GITHUB_REF GITHUB_COMMIT GITHUB_USER GITHUB_WORKFLOW \
223-
TRAVIS_BRANCH TRAVIS_BUILD_WEB_URL TRAVIS_COMMIT TRAVIS_COMMIT_MESSAGE \
224-
APPVEYOR_REPO_BRANCH APPVEYOR_REPO_COMMIT \
225-
APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED
222+
codecov --file="{env:COVERAGE_XML}" --env \
223+
GITHUB_REF GITHUB_COMMIT GITHUB_USER GITHUB_WORKFLOW \
224+
TRAVIS_BRANCH TRAVIS_BUILD_WEB_URL \
225+
TRAVIS_COMMIT TRAVIS_COMMIT_MESSAGE \
226+
APPVEYOR_REPO_BRANCH APPVEYOR_REPO_COMMIT \
227+
APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL \
228+
APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED
229+
230+
231+
##
232+
# Documentation
233+
##
234+
235+
[testenv:docs]
236+
237+
description = build documentation
238+
239+
basepython = python3.8
240+
241+
deps =
242+
Sphinx==2.2.2
243+
sphinx-rtd-theme==0.4.3
244+
245+
commands =
246+
sphinx-build \
247+
-b html -d "{envtmpdir}/doctrees" \
248+
"{toxinidir}/docs" \
249+
"{toxworkdir}/docs/html"
250+
251+
252+
[testenv:docs-auto]
253+
254+
description = build documentation and rebuild automatically
255+
256+
basepython = python3.8
257+
258+
deps =
259+
Sphinx==2.2.2
260+
sphinx-rtd-theme==0.4.3
261+
sphinx-autobuild==0.7.1
262+
263+
commands =
264+
sphinx-autobuild \
265+
-b html -d "{envtmpdir}/doctrees" \
266+
--host=localhost \
267+
"{toxinidir}/docs" \
268+
"{toxworkdir}/docs/html"
226269

227270

228271
##

0 commit comments

Comments
 (0)