pathlib-next 0.1.8__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/PKG-INFO +8 -1
  2. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/pyproject.toml +3 -2
  3. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/example.py +11 -5
  4. pathlib_next-0.2.1/src/pathlib_next/__init__.py +10 -0
  5. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/fspath.py +6 -5
  6. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/path.py +1 -1
  7. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/uri/__init__.py +19 -14
  8. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/uri/query.py +3 -2
  9. pathlib_next-0.2.1/src/pathlib_next/uri/schemes/__init__.py +10 -0
  10. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/uri/schemes/file.py +3 -2
  11. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/uri/schemes/http.py +10 -9
  12. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/uri/schemes/sftp.py +10 -5
  13. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/uri/source.py +4 -3
  14. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/utils/__init__.py +8 -8
  15. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/utils/stat.py +68 -2
  16. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/utils/sync.py +26 -13
  17. pathlib_next-0.2.1/tests/test_uri.py +99 -0
  18. pathlib_next-0.1.8/src/pathlib_next/__init__.py +0 -7
  19. pathlib_next-0.1.8/src/pathlib_next/uri/schemes/__init__.py +0 -10
  20. pathlib_next-0.1.8/tests/test_uri.py +0 -75
  21. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/.gitignore +0 -0
  22. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/LICENSE +0 -0
  23. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/README.md +0 -0
  24. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/mempath.py +0 -0
  25. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/src/pathlib_next/utils/glob.py +0 -0
  26. {pathlib_next-0.1.8 → pathlib_next-0.2.1}/tests/test_local.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pathlib_next
3
- Version: 0.1.8
3
+ Version: 0.2.1
4
4
  Summary: Generic Path Protocol based pathlib
5
5
  Project-URL: Homepage, https://github.com/jose-pr/pathlib_next/
6
6
  Project-URL: Issues, https://github.com/jose-pr/pathlib_next/issues
@@ -10,7 +10,14 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.11
13
+ Provides-Extra: dev
14
+ Requires-Dist: build; extra == 'dev'
15
+ Requires-Dist: hatchling; extra == 'dev'
16
+ Requires-Dist: pytest; extra == 'dev'
17
+ Requires-Dist: twine; extra == 'dev'
13
18
  Provides-Extra: http
19
+ Requires-Dist: bs4; extra == 'http'
20
+ Requires-Dist: htmllistparse; extra == 'http'
14
21
  Requires-Dist: requests; extra == 'http'
15
22
  Requires-Dist: uritools; extra == 'http'
16
23
  Provides-Extra: sftp
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pathlib_next"
7
- version = "0.1.8"
7
+ version = "0.2.1"
8
8
  authors = [{ name = "Jose A" }]
9
9
  description = "Generic Path Protocol based pathlib"
10
10
  readme = "README.md"
@@ -17,8 +17,9 @@ classifiers = [
17
17
  dependencies = []
18
18
  [project.optional-dependencies]
19
19
  uri = ["uritools"]
20
- http = ["requests", "pathlib_next[uri]"]
20
+ http = ["requests", "pathlib_next[uri]", 'htmllistparse', 'bs4']
21
21
  sftp = ["paramiko", "pathlib_next[uri]"]
22
+ dev = ['build', 'twine', 'hatchling', 'pytest']
22
23
 
23
24
 
24
25
  [project.urls]
@@ -1,18 +1,24 @@
1
+ import os
2
+
1
3
  from pathlib_next import Path, glob
2
4
  from pathlib_next.mempath import MemPath
3
- from pathlib_next.uri import Query, Source, UriPath
4
- from pathlib_next.uri.schemes import *
5
+ from pathlib_next.uri import Query, Source, Uri, UriPath
5
6
  from pathlib_next.utils.sync import PathSyncer
6
7
 
8
+ rootless = Uri("sftp://root@sftpexample")
9
+ rootless.source
10
+ authkeys = rootless / "root/.ssh/authorized_keys"
11
+ keys = authkeys.as_uri()
12
+
13
+ local = Path("./_ssh")
14
+
7
15
  mempath = MemPath("test/test3") / "subpath"
8
16
  mempath.parent.mkdir(parents=True, exist_ok=True)
9
17
  mempath.write_text("test")
10
18
  check = mempath.read_text()
11
19
  mempath.parent.rm(recursive=True)
12
20
 
13
- pass
14
-
15
- local = Path("./_ssh")
21
+ test = list(os.scandir(local))
16
22
  print(list(local.iterdir()))
17
23
  query = Query({"test": "://$#!1", "test2&": [1, 2]})
18
24
  q2 = Query(str(query)).to_dict()
@@ -0,0 +1,10 @@
1
+ try:
2
+ from .uri import Uri as Uri
3
+ from .uri import UriPath as UriPath
4
+ from .uri.schemes import *
5
+ except ImportError:
6
+ pass
7
+ from .fspath import *
8
+ from .path import *
9
+ from .utils import glob as glob
10
+ from .utils import sync as sync
@@ -1,9 +1,10 @@
1
- import pathlib as _path
2
- import os as _os
3
1
  import functools as _func
2
+ import os as _os
3
+ import pathlib as _path
4
+ import re as _re
4
5
  import typing as _ty
6
+
5
7
  from . import path as _proto
6
- import re as _re
7
8
 
8
9
 
9
10
  @_func.cache
@@ -32,7 +33,7 @@ class _BaseFSPathname(_path.PurePath, _proto.Pathname):
32
33
  @property
33
34
  def segments(self):
34
35
  return self.parts
35
-
36
+
36
37
  def with_segments(self, *args: str | _proto.FsPathLike):
37
38
  return type(self)(*args)
38
39
 
@@ -79,4 +80,4 @@ class LocalPath(
79
80
  include_hidden=include_hidden,
80
81
  recursive=recursive,
81
82
  dironly=dironly,
82
- )
83
+ )
@@ -177,7 +177,7 @@ class Pathname(FsPathLike, _ty.Generic[_P]):
177
177
  def __truediv__(self, key: _ty.Self | str) -> _ty.Self:
178
178
  try:
179
179
  return type(self)(self, key)
180
- except (TypeError, NotImplementedError):
180
+ except (TypeError, NotImplementedError) as _err:
181
181
  return NotImplemented
182
182
 
183
183
  @property
@@ -137,6 +137,13 @@ class Uri(Pathname):
137
137
  if _path.startswith("/"):
138
138
  break
139
139
 
140
+ if (
141
+ (source.host or source.userinfo or source.port)
142
+ and _path
143
+ and not _path.startswith("/")
144
+ ):
145
+ _path = "/" + _path
146
+
140
147
  self._init(source, _path, query, fragment)
141
148
 
142
149
  def _init(self, source: Source, path: str, query: str, fragment: str, **kwargs):
@@ -201,7 +208,10 @@ class Uri(Pathname):
201
208
  raise NotImplementedError(f"fspath for {self.source.scheme}")
202
209
 
203
210
  def __repr__(self):
204
- return "{}({!r})".format(type(self).__name__, str(self))
211
+ if self._initiated:
212
+ return "{}({!r})".format(type(self).__name__, str(self))
213
+ else:
214
+ return super().__repr__()
205
215
 
206
216
  def as_uri(self, /, sanitize=False):
207
217
  if self._uri is None or sanitize:
@@ -398,6 +408,13 @@ class UriPath(Uri, Path):
398
408
  inst._init(uri.source, uri.path, uri.query, uri.fragment, **kwargs)
399
409
  else:
400
410
  inst = Uri.__new__(cls, *args, **kwargs)
411
+ backend = kwargs.get("backend", None)
412
+ if backend is None:
413
+ for segment in reversed(args):
414
+ if isinstance(segment, cls):
415
+ backend = segment.backend
416
+ break
417
+ inst._backend = backend
401
418
  return inst
402
419
 
403
420
  def _initbackend(self):
@@ -433,18 +450,6 @@ class UriPath(Uri, Path):
433
450
  def with_backend(self, backend):
434
451
  return self._from_parsed_parts(*self.parts, backend=backend)
435
452
 
436
- def _load_parts(self):
437
- super()._load_parts()
438
- if self.source and self.source.scheme and self._backend is None:
439
- for uri in reversed(self._raw_uris):
440
- if (
441
- isinstance(uri, UriPath)
442
- and uri.source.scheme == self.source.scheme
443
- and uri._backend
444
- ):
445
- self._backend = uri._backend
446
- break
447
-
448
453
  def __truediv__(self, key: str | Uri | os.PathLike):
449
454
  try:
450
455
  return type(self)(self, key, findclass=True)
@@ -456,7 +461,7 @@ class UriPath(Uri, Path):
456
461
  if not source:
457
462
  inst = Uri.__new__(UriPath)
458
463
  elif source.scheme not in cls._schemes():
459
- inst = UriPath.__new__(UriPath, source.scheme + ":")
464
+ inst = cls.__new__(cls, source.scheme + ":", findclass=True)
460
465
  else:
461
466
  inst = cls.__new__(cls, backend=self._backend)
462
467
  inst._init(source, self.path, self.query, self.fragment)
@@ -1,4 +1,5 @@
1
1
  import typing as _ty
2
+
2
3
  import uritools as _uritools
3
4
 
4
5
 
@@ -16,7 +17,7 @@ class Query(str):
16
17
  ),
17
18
  *,
18
19
  encoding=ENCODING,
19
- separator=SEPARATOR
20
+ separator=SEPARATOR,
20
21
  ):
21
22
  if isinstance(query, Query):
22
23
  _encoding = query._encoding
@@ -44,7 +45,7 @@ class Query(str):
44
45
  return _uritools.SplitResultString("", "", "", str(query), "").getquerylist(
45
46
  query._separator, query._encoding
46
47
  )
47
-
48
+
48
49
  def __iter__(self):
49
50
  return iter(self.decode())
50
51
 
@@ -0,0 +1,10 @@
1
+ from .file import FileUri as FileUri
2
+
3
+ try:
4
+ from .http import HttpPath as HttpPath
5
+ except ImportError:
6
+ pass
7
+ try:
8
+ from .sftp import SftpPath as SftpPath
9
+ except ImportError:
10
+ pass
@@ -1,7 +1,8 @@
1
+ import os as _os
2
+
1
3
  from ...fspath import LocalPath as _Local
2
4
  from ...path import FsPathLike
3
- import os as _os
4
- from .. import UriPath, Source
5
+ from .. import Source, UriPath
5
6
 
6
7
 
7
8
  class FileUri(UriPath):
@@ -1,14 +1,15 @@
1
- from ...utils.stat import FileStat
2
- from .. import UriPath
3
- from ... import utils as _utils
4
1
  import io as _io
5
- import requests as _req
6
- import typing as _ty
7
- import bs4 as _bs4
8
2
  import time as _time
3
+ import typing as _ty
9
4
 
5
+ import bs4 as _bs4
6
+ import requests as _req
10
7
  from htmllistparse import parse as _htmlparse
11
8
 
9
+ from ... import utils as _utils
10
+ from ...utils.stat import FileStat
11
+ from .. import UriPath
12
+
12
13
 
13
14
  class _FileEntry(_ty.NamedTuple):
14
15
  name: str
@@ -42,7 +43,7 @@ class HttpPath(UriPath):
42
43
  return HttpBackend(_req.Session(), {})
43
44
 
44
45
  def _listdir(self) -> list[_FileEntry]:
45
- req = self.backend.session.request("GET", self)
46
+ req = self.backend.request("GET", self)
46
47
  req.raise_for_status()
47
48
  soup = _bs4.BeautifulSoup(req.content, "html5lib")
48
49
  _, listing = _htmlparse(soup)
@@ -64,7 +65,7 @@ class HttpPath(UriPath):
64
65
  or resp.url.endswith("/.")
65
66
  )
66
67
 
67
- def stat(self):
68
+ def stat(self, *, follow_symlinks=True, walk_up_last_modified=False):
68
69
  check = (
69
70
  [self.with_path(self.path.removesuffix("/")), self]
70
71
  if self.path.endswith("/")
@@ -90,7 +91,7 @@ class HttpPath(UriPath):
90
91
 
91
92
  st_size = 0 if self._isdir else int(resp.headers.get("Content-Length", 0))
92
93
  lm = resp.headers.get("Last-Modified")
93
- if lm is None:
94
+ if lm is None and walk_up_last_modified:
94
95
  parent = self.parent
95
96
  if self != parent:
96
97
  try:
@@ -1,9 +1,11 @@
1
- import typing as _ty
2
- from .. import UriPath, Source
3
- from ... import utils as _utils
4
1
  import threading as _thread
2
+ import typing as _ty
3
+
5
4
  import paramiko as _paramiko
6
5
 
6
+ from ... import utils as _utils
7
+ from .. import Source, UriPath
8
+
7
9
 
8
10
  class BaseSftpBackend(object):
9
11
  __slots__ = ()
@@ -77,8 +79,11 @@ class SftpPath(UriPath):
77
79
  for path in self._sftpclient.listdir(self.path):
78
80
  yield path
79
81
 
80
- def stat(self):
81
- return self._sftpclient.stat(self.path)
82
+ def stat(self, *, follow_symlinks=True):
83
+ if follow_symlinks:
84
+ return self._sftpclient.stat(self.path)
85
+ else:
86
+ return self._sftpclient.lstat(self.path)
82
87
 
83
88
  def _open(self, mode="r", buffering=-1):
84
89
  return self._sftpclient.open(self.path, mode, buffering)
@@ -1,7 +1,8 @@
1
+ import ipaddress as _ip
2
+ import socket as _socket
1
3
  import typing as _ty
4
+
2
5
  import uritools as _uritools
3
- import socket as _socket
4
- import ipaddress as _ip
5
6
 
6
7
  from .. import utils as _utils
7
8
 
@@ -19,7 +20,7 @@ class Source(_ty.NamedTuple):
19
20
 
20
21
  def __bool__(self):
21
22
  for val in self:
22
- if val == '' or val is None:
23
+ if val == "" or val is None:
23
24
  continue
24
25
  return True
25
26
  return False
@@ -1,11 +1,9 @@
1
+ import collections
1
2
  import functools as _functools
2
- from threading import RLock
3
- import typing as _ty
4
3
  import time as _time
4
+ import typing as _ty
5
5
  from email.utils import parsedate as _parsedate
6
-
7
-
8
- import collections
6
+ from threading import RLock
9
7
 
10
8
  K = _ty.ParamSpec("K")
11
9
  V = _ty.TypeVar("V")
@@ -22,7 +20,7 @@ class LRU(_ty.Generic[K, V]):
22
20
  @property
23
21
  def maxsize(self):
24
22
  return self._maxsize
25
-
23
+
26
24
  @maxsize.setter
27
25
  def maxsize(self, maxsize: int):
28
26
  cache = self.cache
@@ -78,11 +76,13 @@ def notimplemented(method):
78
76
 
79
77
  return _notimplemented
80
78
 
81
- import socket as _socket
79
+
82
80
  import ipaddress as _ip
81
+ import socket as _socket
82
+
83
83
 
84
84
  def get_machine_ips():
85
- ips:list[_ip.IPv4Address|_ip.IPv6Address] = list()
85
+ ips: list[_ip.IPv4Address | _ip.IPv6Address] = list()
86
86
  for item in _socket.getaddrinfo(_socket.gethostname(), None):
87
87
  protocol, *_, (ip, *_) = item
88
88
  if protocol == _socket.AddressFamily.AF_INET:
@@ -1,8 +1,15 @@
1
+ import abc as _abc
1
2
  import stat as _stat
2
3
  import typing as _ty
3
- import abc as _abc
4
+
4
5
  from .. import utils as _utils
5
6
 
7
+ if _ty.TYPE_CHECKING:
8
+ from os import stat_result as _os_stat
9
+
10
+ from ..path import Path
11
+
12
+
6
13
  class FileStatLike(_ty.Protocol):
7
14
  __slots__ = ()
8
15
 
@@ -16,6 +23,7 @@ class FileStatLike(_ty.Protocol):
16
23
  @_abc.abstractmethod
17
24
  def st_mtime(self) -> int: ...
18
25
 
26
+
19
27
  class FileStat(FileStatLike):
20
28
  __slots__ = (
21
29
  "st_mode",
@@ -74,4 +82,62 @@ class FileStat(FileStatLike):
74
82
 
75
83
  def __str__(self):
76
84
  props = [f"{k}={v}" for k, v in self.items()]
77
- return "<%s %s>" % (type(self).__name__, ", ".join(props))
85
+ return "<%s %s>" % (type(self).__name__, ", ".join(props))
86
+
87
+ @classmethod
88
+ def from_path(cls, path: "Path", *, follow_symlink=True):
89
+ try:
90
+ stat: _os_stat = path.stat(follow_symlinks=follow_symlink)
91
+ if isinstance(stat, FileStat):
92
+ return stat
93
+ else:
94
+ result = FileStat.__new__(FileStat)
95
+ for prop in FileStat.__slots__:
96
+ val = getattr(stat, prop, 0)
97
+ setattr(result, prop, val)
98
+ return result
99
+ except FileNotFoundError:
100
+ return None
101
+
102
+ def is_dir(self):
103
+ """
104
+ Whether this path is a directory.
105
+ """
106
+ return _stat.S_ISDIR(self.st_mode)
107
+
108
+ def is_file(self):
109
+ """
110
+ Whether this path is a regular file (also True for symlinks pointing
111
+ to regular files).
112
+ """
113
+ return _stat.S_ISREG(self.st_mode)
114
+
115
+ def is_symlink(self):
116
+ """
117
+ Whether this path is a symbolic link.
118
+ """
119
+ return _stat.S_ISLNK(self.st_mode)
120
+
121
+ def is_block_device(self):
122
+ """
123
+ Whether this path is a block device.
124
+ """
125
+ return _stat.S_ISBLK(self.st_mode)
126
+
127
+ def is_char_device(self):
128
+ """
129
+ Whether this path is a character device.
130
+ """
131
+ return _stat.S_ISCHR(self.st_mode)
132
+
133
+ def is_fifo(self):
134
+ """
135
+ Whether this path is a FIFO.
136
+ """
137
+ return _stat.S_ISFIFO(self.st_mode)
138
+
139
+ def is_socket(self):
140
+ """
141
+ Whether this path is a socket.
142
+ """
143
+ return _stat.S_ISSOCK(self.st_mode)
@@ -1,6 +1,8 @@
1
+ import enum as _enum
1
2
  import typing as _ty
3
+
2
4
  from ..path import Path
3
- import enum as _enum
5
+ from ..utils.stat import FileStat
4
6
 
5
7
 
6
8
  class SyncEvent(_enum.Enum):
@@ -12,7 +14,7 @@ class SyncEvent(_enum.Enum):
12
14
 
13
15
 
14
16
  class PathSyncer(object):
15
- __slots__ = ("checksum", "_hook", "remove_missing")
17
+ __slots__ = ("checksum", "_hook", "remove_missing", "follow_symlinks")
16
18
  EVENT_LOG_FORMAT = "[{event}] Source:{source} Target:{target} DryRun:{dry_run}"
17
19
 
18
20
  def __init__(
@@ -20,11 +22,13 @@ class PathSyncer(object):
20
22
  checksum: _ty.Callable[[Path], int],
21
23
  /,
22
24
  remove_missing: bool = False,
25
+ follow_symlinks: bool = True,
23
26
  hook: _ty.Callable[[Path, Path, SyncEvent, bool], None] = None,
24
27
  ) -> None:
25
28
  self.checksum = checksum
26
29
  self.remove_missing = remove_missing
27
30
  self._hook = hook
31
+ self.follow_symlinks = follow_symlinks
28
32
 
29
33
  def log(self, msg: str, **kwargs: str):
30
34
  print(msg.format_map(kwargs))
@@ -46,34 +50,43 @@ class PathSyncer(object):
46
50
  dry_run=dry_run,
47
51
  )
48
52
 
49
- def sync(
50
- self, source: Path, target: Path, /, dry_run: bool = False
51
- ):
53
+ def sync(self, source: Path, target: Path, /, dry_run: bool = False):
52
54
  checksum = self.checksum
53
55
  self.hook(source, target, SyncEvent.SyncStart, dry_run)
54
56
 
55
- if not source.exists():
57
+ src_stat = FileStat.from_path(source, follow_symlink=self.follow_symlinks)
58
+ tgt_stat = FileStat.from_path(target, follow_symlink=self.follow_symlinks)
59
+
60
+ if src_stat is None:
56
61
  if self.remove_missing:
57
62
  if not dry_run:
58
63
  target.rm(recursive=True, missing_ok=True)
59
64
  self.hook(source, target, SyncEvent.RemovedMissing, dry_run)
60
-
61
- elif source.is_file():
65
+ elif src_stat.is_symlink():
66
+ raise NotImplementedError("symlink sync not implemented yet")
67
+ elif src_stat.is_file():
62
68
  synced = False
63
- if target.is_file():
69
+ if tgt_stat and tgt_stat.is_file():
64
70
  if checksum(target) == checksum(source):
65
71
  synced = True
66
72
  if not synced:
67
73
  if not dry_run:
68
- target.rm(recursive=True, missing_ok=True)
74
+ if tgt_stat:
75
+ if tgt_stat.is_file() or tgt_stat.is_symlink():
76
+ target.unlink()
77
+ else:
78
+ if target:
79
+ target.rm(recursive=tgt_stat.is_dir())
69
80
  source.copy(target)
70
81
  self.hook(source, target, SyncEvent.Copy, dry_run)
71
82
  else:
72
- if target.is_file():
83
+ t_exists = tgt_stat != None
84
+ if tgt_stat and tgt_stat.is_file():
73
85
  if not dry_run:
74
86
  target.unlink()
87
+ t_exists = False
75
88
 
76
- if not target.exists():
89
+ if not t_exists:
77
90
  if not dry_run:
78
91
  target.mkdir()
79
92
  self.hook(source, target, SyncEvent.CreatedDirectory, dry_run)
@@ -84,7 +97,7 @@ class PathSyncer(object):
84
97
  if not dry_run:
85
98
  child.rm(recursive=True)
86
99
  self.hook(source, target, SyncEvent.RemovedMissing, dry_run)
87
-
100
+
88
101
  for child in source.iterdir():
89
102
  self.sync(child, target / child.name, dry_run)
90
103
 
@@ -0,0 +1,99 @@
1
+ import pathlib
2
+
3
+ import pytest
4
+
5
+ import src.pathlib_next as pathlib_next
6
+ from src.pathlib_next.uri import Uri
7
+
8
+ test_uris = ["http://user:pass@google.com:80"]
9
+
10
+
11
+ # @pytest.mark.parametrize("_uri", test_uris)
12
+ def parse_uri(_uri: str):
13
+ uri = pathlib_next.Uri(_uri)
14
+ assert uri.as_uri() == _uri
15
+ return uri
16
+
17
+
18
+ def test_source():
19
+ uri = parse_uri(test_uris[0])
20
+ assert uri.source.scheme == "http"
21
+ assert uri.source.host == "google.com"
22
+ assert uri.source.port == 80
23
+ assert uri.source.parsed_userinfo() == ("user", "pass")
24
+
25
+
26
+ def test_no_scheme():
27
+ uri = parse_uri("//user:pass@google.com:80/")
28
+ assert uri.source.scheme == None
29
+ assert uri.source.host == "google.com"
30
+ assert uri.source.port == 80
31
+ assert uri.source.parsed_userinfo() == ("user", "pass")
32
+
33
+
34
+ def test_no_scheme_with_host_no_pass():
35
+ uri = parse_uri("//user@google.com:80/")
36
+ assert uri.source.scheme == None
37
+ assert uri.source.host == "google.com"
38
+ assert uri.source.port == 80
39
+ assert uri.source.parsed_userinfo() == ("user", "")
40
+
41
+
42
+ def test_no_scheme_no_host():
43
+ uri = parse_uri("//user@:80/")
44
+ assert uri.source.scheme == None
45
+ assert uri.source.host == ""
46
+ assert uri.source.port == 80
47
+ assert uri.source.parsed_userinfo() == ("user", "")
48
+
49
+
50
+ def test_no_scheme_no_netloc():
51
+ uri = parse_uri("//user@")
52
+ assert uri.source.scheme == None
53
+ assert uri.source.host == ""
54
+ assert uri.source.port == None
55
+ assert uri.source.parsed_userinfo() == ("user", "")
56
+
57
+
58
+ def test_path():
59
+ uri = parse_uri("http://google.com/root/subroot/filename.ext")
60
+ assert uri.source.scheme == "http"
61
+ assert uri.source.host == "google.com"
62
+ assert uri.source.port == None
63
+ assert uri.source.parsed_userinfo() == ("", "")
64
+ assert uri.path == "/root/subroot/filename.ext"
65
+
66
+
67
+ def test_encoded_path():
68
+ uri = pathlib_next.Uri(
69
+ "http://goog%2Fe.com/root/subroot/%3Fquery/%23fragment/%2Fencoded%2Ffilename.ext"
70
+ )
71
+ assert uri.source.scheme == "http"
72
+ assert uri.source.host == "goog/e.com"
73
+ assert uri.source.port == None
74
+ assert uri.source.parsed_userinfo() == ("", "")
75
+ assert uri.path == "/root/subroot/?query/#fragment//encoded/filename.ext"
76
+
77
+
78
+ def test_child():
79
+ sftp_root = Uri("sftp://root@sftpexample/")
80
+ authkeys = sftp_root / "root/.ssh/authorized_keys"
81
+ uri = authkeys.as_uri()
82
+ assert uri == "sftp://root@sftpexample/root/.ssh/authorized_keys"
83
+
84
+
85
+ def test_truediv_pathlib():
86
+ sftp_root = Uri("sftp://root@sftpexample/")
87
+ authkeys = sftp_root / pathlib.PurePosixPath("root/.ssh/authorized_keys")
88
+ uri = authkeys.as_uri()
89
+ assert uri == "sftp://root@sftpexample/root/.ssh/authorized_keys"
90
+
91
+ authkeys = sftp_root / pathlib.Path("root/.ssh/authorized_keys")
92
+ uri = authkeys.as_uri()
93
+ assert uri == "file:/root/.ssh/authorized_keys"
94
+
95
+
96
+ def test_join_without_root():
97
+ authkeys = Uri("sftp://root@sftpexample") / "root/.ssh/authorized_keys"
98
+ uri = authkeys.as_uri()
99
+ assert uri == "sftp://root@sftpexample/root/.ssh/authorized_keys"
@@ -1,7 +0,0 @@
1
- try:
2
- from .uri import UriPath, Uri
3
- except ImportError:
4
- pass
5
- from .path import *
6
- from .fspath import *
7
- from .utils import glob, sync
@@ -1,10 +0,0 @@
1
- from .file import FileUri
2
-
3
- try:
4
- from .http import HttpPath
5
- except ImportError:
6
- pass
7
- try:
8
- from .sftp import SftpPath
9
- except ImportError:
10
- pass
@@ -1,75 +0,0 @@
1
- import pytest
2
- import src.pathlib_next as pathlib_next
3
- from src.pathlib_next.uri import Uri
4
-
5
- import pathlib
6
-
7
-
8
- def test_source():
9
- uri = pathlib_next.Uri("http://user:pass@google.com:80")
10
- assert uri.source.scheme == "http"
11
- assert uri.source.host == "google.com"
12
- assert uri.source.port == 80
13
- assert uri.source.parsed_userinfo() == ('user','pass')
14
-
15
-
16
- def test_no_scheme():
17
- uri = pathlib_next.Uri("//user:pass@google.com:80/")
18
- assert uri.source.scheme == None
19
- assert uri.source.host == "google.com"
20
- assert uri.source.port == 80
21
- assert uri.source.parsed_userinfo() == ('user','pass')
22
-
23
- def test_no_scheme_with_host_no_pass():
24
- uri = pathlib_next.Uri("//user@google.com:80/")
25
- assert uri.source.scheme == None
26
- assert uri.source.host == "google.com"
27
- assert uri.source.port == 80
28
- assert uri.source.parsed_userinfo() == ('user', '')
29
-
30
- def test_no_scheme_no_host():
31
- uri = pathlib_next.Uri("//user@:80/")
32
- assert uri.source.scheme == None
33
- assert uri.source.host == ""
34
- assert uri.source.port == 80
35
- assert uri.source.parsed_userinfo() == ('user', '')
36
-
37
- def test_no_scheme_no_netloc():
38
- uri = pathlib_next.Uri("//user@")
39
- assert uri.source.scheme == None
40
- assert uri.source.host == ""
41
- assert uri.source.port == None
42
- assert uri.source.parsed_userinfo() == ('user', '')
43
-
44
- def test_path():
45
- uri = pathlib_next.Uri("http://google.com/root/subroot/filename.ext")
46
- assert uri.source.scheme == "http"
47
- assert uri.source.host == "google.com"
48
- assert uri.source.port == None
49
- assert uri.source.parsed_userinfo() == ('','')
50
- assert uri.path == '/root/subroot/filename.ext'
51
-
52
- def test_encoded_path():
53
- uri = pathlib_next.Uri("http://goog%2Fe.com/root/subroot/%3Fquery/%23fragment/%2Fencoded%2Ffilename.ext")
54
- assert uri.source.scheme == "http"
55
- assert uri.source.host == "goog/e.com"
56
- assert uri.source.port == None
57
- assert uri.source.parsed_userinfo() == ('','')
58
- assert uri.path == '/root/subroot/?query/#fragment//encoded/filename.ext'
59
-
60
-
61
- def test_child():
62
- sftp_root = Uri('sftp://root@sftpexample/')
63
- authkeys = sftp_root / 'root/.ssh/authorized_keys'
64
- uri = authkeys.as_uri()
65
- assert uri == 'sftp://root@sftpexample/root/.ssh/authorized_keys'
66
-
67
- def test_truediv_pathlib():
68
- sftp_root = Uri('sftp://root@sftpexample/')
69
- authkeys = sftp_root / pathlib.PurePosixPath('root/.ssh/authorized_keys')
70
- uri = authkeys.as_uri()
71
- assert uri == 'sftp://root@sftpexample/root/.ssh/authorized_keys'
72
- authkeys = sftp_root / pathlib.Path('root/.ssh/authorized_keys')
73
- uri = authkeys.as_uri()
74
- assert uri == 'file:/root/.ssh/authorized_keys'
75
-
File without changes
File without changes
File without changes