Edit on GitHub

gpath.render

  1from __future__ import annotations
  2
  3import functools
  4from abc import ABC, abstractmethod
  5from typing import Type
  6
  7from . import _rules
  8from .platform import Platform
  9
 10
 11__all__ = (
 12	'Renderable',
 13	'RenderedPath',
 14	'GenericRenderedPath',
 15	'PosixRenderedPath',
 16	'LinuxRenderedPath',
 17	'MacOsRenderedPath',
 18	'WindowsRenderedPath',
 19)
 20
 21
 22class Renderable(ABC):
 23	"""
 24		Abstract interface that represents any object that can be converted to a RenderedPath.
 25
 26		Abstract properties
 27		-------------------
 28		`named_parts: list[str]`
 29		: read-only named components of the path, not including the filesystem root, drive name, or any parent directories
 30
 31		`relative_parts: list[str]`
 32		: read-only relative components of the path, not including the filesystem root or drive name, including one item for each level of parent directory
 33
 34		`absolute: bool`
 35		: read-only flag for whether the path is an absolute path
 36
 37		`parent_level: int`
 38		: read-only number of levels of parent directories that the path is relative to, which may be 0
 39	"""
 40
 41	@property
 42	@abstractmethod
 43	def named_parts(self) -> list[str]:
 44		pass
 45
 46	@property
 47	@abstractmethod
 48	def relative_parts(self) -> list[str]:
 49		pass
 50
 51	@property
 52	@abstractmethod
 53	def absolute(self) -> bool:
 54		pass
 55
 56	@property
 57	@abstractmethod
 58	def drive(self) -> str:
 59		pass
 60
 61	@property
 62	@abstractmethod
 63	def parent_level(self) -> int:
 64		pass
 65
 66	@abstractmethod
 67	def __repr__(self) -> str:
 68		"""
 69			Return a string representation of the object instance for debugging
 70		"""
 71		pass
 72
 73
 74@functools.total_ordering
 75class RenderedPath(ABC):
 76	"""
 77		Abstract base class for rendered path objects that target a specific operating system.
 78
 79		Whereas GPath represents a generalised abstract path, RenderedPath represents a file path on a specific platform with properties defined by the specific type of filesystem. Note however that the RenderedPath still does not represent a real file in a real filesystem, and can represent file paths on a system other than local.
 80
 81		The additional semantics available to RenderedPath allows it to be:
 82		- printed in a format preferred by the given platform
 83		- meaningfully compared and sorted
 84	"""
 85
 86	__slots__ = ('_path')
 87
 88	def __hash__(self) -> int:
 89		"""
 90			Calculate hash of the RenderedPath.
 91
 92			Usage: <code>hash(<var>rp</var>)</code>
 93		"""
 94		return hash(self._tuple)
 95
 96	def __init__(self, path: Renderable):
 97		"""
 98			Initialise a rendered path from any object that is Renderable.
 99		"""
100		self._path: Renderable = path
101
102	def __eq__(self, other) -> bool:
103		"""
104			Check if two RenderedPaths have the same target platform, and check if they have equivalent values on that platform
105
106			Usage: <code><var>rp1</var> == <var>rp2</var></code>
107		"""
108		return type(self) == type(other) and self._tuple == other._tuple
109
110	def __lt__(self, other) -> bool:
111		"""
112			Check if `self` should be collated before `other`
113
114			Usage: <code><var>rp1</var> < <var>rp2</var></code>
115		"""
116		return self._tuple < other._tuple
117
118	def __bool__(self) -> bool:
119		"""
120			False if `self` is equivalent to an empty path on the target platform, and True otherwise
121
122			By default, False if `self` is a relative path without any relative components and without a drive, and True otherwise.
123
124			Usage: <code>bool(<var>rp</var>)</code>, <code>not <var>rp</var></code>, or <code>if <var>rp</var>:</code>
125		"""
126		return self._path.absolute or self._path.drive != "" or self._path.parent_level != 0 or len(self._path.named_parts) > 0
127
128	def __str__(self) -> str:
129		"""
130			Return a string representation of the path in the preferred format for the target platform
131
132			Usage: <code>str(<var>rp</var>)</code>
133		"""
134		return repr(self)
135
136	def __repr__(self) -> str:
137		"""
138			Return a string that, when printed, gives the Python code associated with instantiating a copy of `self`.
139
140			Usage: <code>repr(<var>rp</var>)</code>
141		"""
142		return f"{type(self).__name__}({repr(self._path)})"
143
144	@property
145	def _tuple(self) -> tuple:
146		return (
147			self._path.absolute,
148			self._path.drive,
149			self._path.parent_level,
150			self._path.named_parts,
151		)
152
153
154class GenericRenderedPath(RenderedPath):
155	"""
156		A rendered path that maximises interoperability between different target platforms, specifically between Windows and POSIX-like operating systems.
157
158		This is done at the expense of producing outputs that may not conform to platform recommendations but that should still be usable across different platforms.
159
160		Note that if the path contains a drive, it should be removed if the path is to be used on Linux or macOS. On Windows, forward slashes / will be used in favour of backslashes.
161	"""
162	def __str__(self) -> str:
163		if bool(self):
164			return (self._path.drive + _rules.generic_rules.drive_postfixes[0] if self._path.drive != "" else "") + (_rules.generic_rules.roots[0] if self._path.absolute else "") + _rules.generic_rules.separators[0].join(self._path.relative_parts)
165		else:
166			return _rules.generic_rules.current_indicators[0]
167
168
169class PosixRenderedPath(RenderedPath):
170	"""
171		A rendered path meant for POSIX-like operating systems, such as Linux and macOS.
172
173		If the original path contains a drive, it will be ignored for both printing and collation. Forward slashes are used always.
174	"""
175	def __str__(self) -> str:
176		if bool(self):
177			return (_rules.posix_rules.roots[0] if self._path.absolute else "") + _rules.posix_rules.separators[0].join(self._path.relative_parts)
178		else:
179			return _rules.posix_rules.current_indicators[0]
180
181	def __bool__(self) -> bool:
182		"""
183			False if `self` is a relative path without any relative components, and True otherwise.
184
185			Usage: <code>bool(<var>rp</var>)</code>, <code>not <var>rp</var></code>, or <code>if <var>rp</var>:</code>
186		"""
187		return self._path.absolute or self._path.parent_level != 0 or len(self._path.named_parts) > 0
188
189	@property
190	def _tuple(self) -> tuple:
191		return (
192			self._path.absolute,
193			self._path.parent_level,
194			self._path.named_parts,
195		)
196
197LinuxRenderedPath = PosixRenderedPath
198"""Alias of `PosixRenderedPath`"""
199
200MacOsRenderedPath = PosixRenderedPath
201"""Alias of `PosixRenderedPath`"""
202
203
204class WindowsRenderedPath(RenderedPath):
205	"""
206		A rendered path meant for Windows operating systems.
207
208		The path may or may not contain a drive, which affects both its printed output and its collation order. Backslashes are used always, although forward slashes are supported on Windows NT also.
209	"""
210	def __str__(self) -> str:
211		if bool(self):
212			return (self._path.drive + _rules.windows_rules.drive_postfixes[0] if self._path.drive != "" else "") + (_rules.windows_rules.roots[0] if self._path.absolute else "") + _rules.windows_rules.separators[0].join(self._path.relative_parts)
213		else:
214			return _rules.windows_rules.current_indicators[0]
215
216
217_render_of_platforms: dict[Platform, Type[RenderedPath]] = {
218	Platform.GENERIC: GenericRenderedPath,
219	Platform.POSIX: PosixRenderedPath,
220	Platform.WINDOWS: WindowsRenderedPath,
221}
222
223
224def get_type(platform: Platform) -> Type[RenderedPath]:
225	"""Get the type of RenderedPath that corresponds to the given Platform"""
226	return _render_of_platforms[platform]
class Renderable(abc.ABC):
23class Renderable(ABC):
24	"""
25		Abstract interface that represents any object that can be converted to a RenderedPath.
26
27		Abstract properties
28		-------------------
29		`named_parts: list[str]`
30		: read-only named components of the path, not including the filesystem root, drive name, or any parent directories
31
32		`relative_parts: list[str]`
33		: read-only relative components of the path, not including the filesystem root or drive name, including one item for each level of parent directory
34
35		`absolute: bool`
36		: read-only flag for whether the path is an absolute path
37
38		`parent_level: int`
39		: read-only number of levels of parent directories that the path is relative to, which may be 0
40	"""
41
42	@property
43	@abstractmethod
44	def named_parts(self) -> list[str]:
45		pass
46
47	@property
48	@abstractmethod
49	def relative_parts(self) -> list[str]:
50		pass
51
52	@property
53	@abstractmethod
54	def absolute(self) -> bool:
55		pass
56
57	@property
58	@abstractmethod
59	def drive(self) -> str:
60		pass
61
62	@property
63	@abstractmethod
64	def parent_level(self) -> int:
65		pass
66
67	@abstractmethod
68	def __repr__(self) -> str:
69		"""
70			Return a string representation of the object instance for debugging
71		"""
72		pass

Abstract interface that represents any object that can be converted to a RenderedPath.

Abstract properties

named_parts: list[str] : read-only named components of the path, not including the filesystem root, drive name, or any parent directories

relative_parts: list[str] : read-only relative components of the path, not including the filesystem root or drive name, including one item for each level of parent directory

absolute: bool : read-only flag for whether the path is an absolute path

parent_level: int : read-only number of levels of parent directories that the path is relative to, which may be 0

@abstractmethod
def __repr__(self) -> str:
67	@abstractmethod
68	def __repr__(self) -> str:
69		"""
70			Return a string representation of the object instance for debugging
71		"""
72		pass

Return a string representation of the object instance for debugging

Inherited Members
builtins.object
__new__
__hash__
__str__
__getattribute__
__setattr__
__delattr__
__lt__
__le__
__eq__
__ne__
__gt__
__ge__
__reduce_ex__
__reduce__
__getstate__
__subclasshook__
__init_subclass__
__format__
__sizeof__
__dir__
@functools.total_ordering
class RenderedPath(abc.ABC):
 75@functools.total_ordering
 76class RenderedPath(ABC):
 77	"""
 78		Abstract base class for rendered path objects that target a specific operating system.
 79
 80		Whereas GPath represents a generalised abstract path, RenderedPath represents a file path on a specific platform with properties defined by the specific type of filesystem. Note however that the RenderedPath still does not represent a real file in a real filesystem, and can represent file paths on a system other than local.
 81
 82		The additional semantics available to RenderedPath allows it to be:
 83		- printed in a format preferred by the given platform
 84		- meaningfully compared and sorted
 85	"""
 86
 87	__slots__ = ('_path')
 88
 89	def __hash__(self) -> int:
 90		"""
 91			Calculate hash of the RenderedPath.
 92
 93			Usage: <code>hash(<var>rp</var>)</code>
 94		"""
 95		return hash(self._tuple)
 96
 97	def __init__(self, path: Renderable):
 98		"""
 99			Initialise a rendered path from any object that is Renderable.
100		"""
101		self._path: Renderable = path
102
103	def __eq__(self, other) -> bool:
104		"""
105			Check if two RenderedPaths have the same target platform, and check if they have equivalent values on that platform
106
107			Usage: <code><var>rp1</var> == <var>rp2</var></code>
108		"""
109		return type(self) == type(other) and self._tuple == other._tuple
110
111	def __lt__(self, other) -> bool:
112		"""
113			Check if `self` should be collated before `other`
114
115			Usage: <code><var>rp1</var> < <var>rp2</var></code>
116		"""
117		return self._tuple < other._tuple
118
119	def __bool__(self) -> bool:
120		"""
121			False if `self` is equivalent to an empty path on the target platform, and True otherwise
122
123			By default, False if `self` is a relative path without any relative components and without a drive, and True otherwise.
124
125			Usage: <code>bool(<var>rp</var>)</code>, <code>not <var>rp</var></code>, or <code>if <var>rp</var>:</code>
126		"""
127		return self._path.absolute or self._path.drive != "" or self._path.parent_level != 0 or len(self._path.named_parts) > 0
128
129	def __str__(self) -> str:
130		"""
131			Return a string representation of the path in the preferred format for the target platform
132
133			Usage: <code>str(<var>rp</var>)</code>
134		"""
135		return repr(self)
136
137	def __repr__(self) -> str:
138		"""
139			Return a string that, when printed, gives the Python code associated with instantiating a copy of `self`.
140
141			Usage: <code>repr(<var>rp</var>)</code>
142		"""
143		return f"{type(self).__name__}({repr(self._path)})"
144
145	@property
146	def _tuple(self) -> tuple:
147		return (
148			self._path.absolute,
149			self._path.drive,
150			self._path.parent_level,
151			self._path.named_parts,
152		)

Abstract base class for rendered path objects that target a specific operating system.

Whereas GPath represents a generalised abstract path, RenderedPath represents a file path on a specific platform with properties defined by the specific type of filesystem. Note however that the RenderedPath still does not represent a real file in a real filesystem, and can represent file paths on a system other than local.

The additional semantics available to RenderedPath allows it to be:

  • printed in a format preferred by the given platform
  • meaningfully compared and sorted
RenderedPath(path: render.Renderable)
 97	def __init__(self, path: Renderable):
 98		"""
 99			Initialise a rendered path from any object that is Renderable.
100		"""
101		self._path: Renderable = path

Initialise a rendered path from any object that is Renderable.

def __hash__(self) -> int:
89	def __hash__(self) -> int:
90		"""
91			Calculate hash of the RenderedPath.
92
93			Usage: <code>hash(<var>rp</var>)</code>
94		"""
95		return hash(self._tuple)

Calculate hash of the RenderedPath.

Usage: hash(rp)

def __eq__(self, other) -> bool:
103	def __eq__(self, other) -> bool:
104		"""
105			Check if two RenderedPaths have the same target platform, and check if they have equivalent values on that platform
106
107			Usage: <code><var>rp1</var> == <var>rp2</var></code>
108		"""
109		return type(self) == type(other) and self._tuple == other._tuple

Check if two RenderedPaths have the same target platform, and check if they have equivalent values on that platform

Usage: rp1 == rp2

def __lt__(self, other) -> bool:
111	def __lt__(self, other) -> bool:
112		"""
113			Check if `self` should be collated before `other`
114
115			Usage: <code><var>rp1</var> < <var>rp2</var></code>
116		"""
117		return self._tuple < other._tuple

Check if self should be collated before other

Usage: rp1 < rp2

def __bool__(self) -> bool:
119	def __bool__(self) -> bool:
120		"""
121			False if `self` is equivalent to an empty path on the target platform, and True otherwise
122
123			By default, False if `self` is a relative path without any relative components and without a drive, and True otherwise.
124
125			Usage: <code>bool(<var>rp</var>)</code>, <code>not <var>rp</var></code>, or <code>if <var>rp</var>:</code>
126		"""
127		return self._path.absolute or self._path.drive != "" or self._path.parent_level != 0 or len(self._path.named_parts) > 0

False if self is equivalent to an empty path on the target platform, and True otherwise

By default, False if self is a relative path without any relative components and without a drive, and True otherwise.

Usage: bool(rp), not rp, or if rp:

def __str__(self) -> str:
129	def __str__(self) -> str:
130		"""
131			Return a string representation of the path in the preferred format for the target platform
132
133			Usage: <code>str(<var>rp</var>)</code>
134		"""
135		return repr(self)

Return a string representation of the path in the preferred format for the target platform

Usage: str(rp)

def __repr__(self) -> str:
137	def __repr__(self) -> str:
138		"""
139			Return a string that, when printed, gives the Python code associated with instantiating a copy of `self`.
140
141			Usage: <code>repr(<var>rp</var>)</code>
142		"""
143		return f"{type(self).__name__}({repr(self._path)})"

Return a string that, when printed, gives the Python code associated with instantiating a copy of self.

Usage: repr(rp)

def __gt__(self, other):
91def _gt_from_lt(self, other):
92    'Return a > b.  Computed by @total_ordering from (not a < b) and (a != b).'
93    op_result = type(self).__lt__(self, other)
94    if op_result is NotImplemented:
95        return op_result
96    return not op_result and self != other

Return a > b. Computed by @total_ordering from (not a < b) and (a != b).

def __le__(self, other):
 98def _le_from_lt(self, other):
 99    'Return a <= b.  Computed by @total_ordering from (a < b) or (a == b).'
100    op_result = type(self).__lt__(self, other)
101    if op_result is NotImplemented:
102        return op_result
103    return op_result or self == other

Return a <= b. Computed by @total_ordering from (a < b) or (a == b).

def __ge__(self, other):
105def _ge_from_lt(self, other):
106    'Return a >= b.  Computed by @total_ordering from (not a < b).'
107    op_result = type(self).__lt__(self, other)
108    if op_result is NotImplemented:
109        return op_result
110    return not op_result

Return a >= b. Computed by @total_ordering from (not a < b).

Inherited Members
builtins.object
__new__
__getattribute__
__setattr__
__delattr__
__ne__
__reduce_ex__
__reduce__
__getstate__
__subclasshook__
__init_subclass__
__format__
__sizeof__
__dir__
class GenericRenderedPath(RenderedPath):
155class GenericRenderedPath(RenderedPath):
156	"""
157		A rendered path that maximises interoperability between different target platforms, specifically between Windows and POSIX-like operating systems.
158
159		This is done at the expense of producing outputs that may not conform to platform recommendations but that should still be usable across different platforms.
160
161		Note that if the path contains a drive, it should be removed if the path is to be used on Linux or macOS. On Windows, forward slashes / will be used in favour of backslashes.
162	"""
163	def __str__(self) -> str:
164		if bool(self):
165			return (self._path.drive + _rules.generic_rules.drive_postfixes[0] if self._path.drive != "" else "") + (_rules.generic_rules.roots[0] if self._path.absolute else "") + _rules.generic_rules.separators[0].join(self._path.relative_parts)
166		else:
167			return _rules.generic_rules.current_indicators[0]

A rendered path that maximises interoperability between different target platforms, specifically between Windows and POSIX-like operating systems.

This is done at the expense of producing outputs that may not conform to platform recommendations but that should still be usable across different platforms.

Note that if the path contains a drive, it should be removed if the path is to be used on Linux or macOS. On Windows, forward slashes / will be used in favour of backslashes.

def __str__(self) -> str:
163	def __str__(self) -> str:
164		if bool(self):
165			return (self._path.drive + _rules.generic_rules.drive_postfixes[0] if self._path.drive != "" else "") + (_rules.generic_rules.roots[0] if self._path.absolute else "") + _rules.generic_rules.separators[0].join(self._path.relative_parts)
166		else:
167			return _rules.generic_rules.current_indicators[0]

Return a string representation of the path in the preferred format for the target platform

Usage: str(rp)

Inherited Members
RenderedPath
RenderedPath
__hash__
__eq__
__lt__
__bool__
__repr__
__gt__
__le__
__ge__
builtins.object
__new__
__getattribute__
__setattr__
__delattr__
__ne__
__reduce_ex__
__reduce__
__getstate__
__subclasshook__
__init_subclass__
__format__
__sizeof__
__dir__
class PosixRenderedPath(RenderedPath):
170class PosixRenderedPath(RenderedPath):
171	"""
172		A rendered path meant for POSIX-like operating systems, such as Linux and macOS.
173
174		If the original path contains a drive, it will be ignored for both printing and collation. Forward slashes are used always.
175	"""
176	def __str__(self) -> str:
177		if bool(self):
178			return (_rules.posix_rules.roots[0] if self._path.absolute else "") + _rules.posix_rules.separators[0].join(self._path.relative_parts)
179		else:
180			return _rules.posix_rules.current_indicators[0]
181
182	def __bool__(self) -> bool:
183		"""
184			False if `self` is a relative path without any relative components, and True otherwise.
185
186			Usage: <code>bool(<var>rp</var>)</code>, <code>not <var>rp</var></code>, or <code>if <var>rp</var>:</code>
187		"""
188		return self._path.absolute or self._path.parent_level != 0 or len(self._path.named_parts) > 0
189
190	@property
191	def _tuple(self) -> tuple:
192		return (
193			self._path.absolute,
194			self._path.parent_level,
195			self._path.named_parts,
196		)

A rendered path meant for POSIX-like operating systems, such as Linux and macOS.

If the original path contains a drive, it will be ignored for both printing and collation. Forward slashes are used always.

def __str__(self) -> str:
176	def __str__(self) -> str:
177		if bool(self):
178			return (_rules.posix_rules.roots[0] if self._path.absolute else "") + _rules.posix_rules.separators[0].join(self._path.relative_parts)
179		else:
180			return _rules.posix_rules.current_indicators[0]

Return a string representation of the path in the preferred format for the target platform

Usage: str(rp)

def __bool__(self) -> bool:
182	def __bool__(self) -> bool:
183		"""
184			False if `self` is a relative path without any relative components, and True otherwise.
185
186			Usage: <code>bool(<var>rp</var>)</code>, <code>not <var>rp</var></code>, or <code>if <var>rp</var>:</code>
187		"""
188		return self._path.absolute or self._path.parent_level != 0 or len(self._path.named_parts) > 0

False if self is a relative path without any relative components, and True otherwise.

Usage: bool(rp), not rp, or if rp:

Inherited Members
RenderedPath
RenderedPath
__hash__
__eq__
__lt__
__repr__
__gt__
__le__
__ge__
builtins.object
__new__
__getattribute__
__setattr__
__delattr__
__ne__
__reduce_ex__
__reduce__
__getstate__
__subclasshook__
__init_subclass__
__format__
__sizeof__
__dir__
LinuxRenderedPath = <class 'render.PosixRenderedPath'>
MacOsRenderedPath = <class 'render.PosixRenderedPath'>
class WindowsRenderedPath(RenderedPath):
205class WindowsRenderedPath(RenderedPath):
206	"""
207		A rendered path meant for Windows operating systems.
208
209		The path may or may not contain a drive, which affects both its printed output and its collation order. Backslashes are used always, although forward slashes are supported on Windows NT also.
210	"""
211	def __str__(self) -> str:
212		if bool(self):
213			return (self._path.drive + _rules.windows_rules.drive_postfixes[0] if self._path.drive != "" else "") + (_rules.windows_rules.roots[0] if self._path.absolute else "") + _rules.windows_rules.separators[0].join(self._path.relative_parts)
214		else:
215			return _rules.windows_rules.current_indicators[0]

A rendered path meant for Windows operating systems.

The path may or may not contain a drive, which affects both its printed output and its collation order. Backslashes are used always, although forward slashes are supported on Windows NT also.

def __str__(self) -> str:
211	def __str__(self) -> str:
212		if bool(self):
213			return (self._path.drive + _rules.windows_rules.drive_postfixes[0] if self._path.drive != "" else "") + (_rules.windows_rules.roots[0] if self._path.absolute else "") + _rules.windows_rules.separators[0].join(self._path.relative_parts)
214		else:
215			return _rules.windows_rules.current_indicators[0]

Return a string representation of the path in the preferred format for the target platform

Usage: str(rp)

Inherited Members
RenderedPath
RenderedPath
__hash__
__eq__
__lt__
__bool__
__repr__
__gt__
__le__
__ge__
builtins.object
__new__
__getattribute__
__setattr__
__delattr__
__ne__
__reduce_ex__
__reduce__
__getstate__
__subclasshook__
__init_subclass__
__format__
__sizeof__
__dir__