gophermap 0.2.0__tar.gz → 1.1.0__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.
- {gophermap-0.2.0 → gophermap-1.1.0}/PKG-INFO +2 -2
- gophermap-1.1.0/pyproject.toml +104 -0
- gophermap-0.2.0/pyproject.toml → gophermap-1.1.0/pyproject.toml.orig +2 -2
- {gophermap-0.2.0 → gophermap-1.1.0}/src/gophermap/__init__.py +1 -2
- gophermap-1.1.0/src/gophermap/exceptions.py +24 -0
- gophermap-1.1.0/src/gophermap/gopher_map.py +117 -0
- {gophermap-0.2.0 → gophermap-1.1.0}/src/gophermap/item.py +13 -1
- {gophermap-0.2.0 → gophermap-1.1.0}/src/gophermap/item_type.py +6 -0
- gophermap-0.2.0/src/gophermap/exceptions.py +0 -28
- gophermap-0.2.0/src/gophermap/gopher_map.py +0 -57
- {gophermap-0.2.0 → gophermap-1.1.0}/README.md +0 -0
- {gophermap-0.2.0 → gophermap-1.1.0}/src/gophermap/__main__.py +0 -0
- {gophermap-0.2.0 → gophermap-1.1.0}/src/gophermap/py.typed +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gophermap
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: A simple library for parsng Gophermaps
|
|
5
5
|
Keywords: Gopher,Gophermap,Hypertext,library,Markup,parser,Small Web,smolweb
|
|
6
6
|
Author: Dave Pearson
|
|
7
7
|
Author-email: Dave Pearson <davep@davep.org>
|
|
8
8
|
License-Expression: MIT
|
|
9
|
-
Classifier: Development Status ::
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gophermap"
|
|
3
|
+
version = "1.1.0"
|
|
4
|
+
description = "A simple library for parsng Gophermaps"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = []
|
|
8
|
+
license = "MIT"
|
|
9
|
+
keywords = [
|
|
10
|
+
"Gopher",
|
|
11
|
+
"Gophermap",
|
|
12
|
+
"Hypertext",
|
|
13
|
+
"library",
|
|
14
|
+
"Markup",
|
|
15
|
+
"parser",
|
|
16
|
+
"Small Web",
|
|
17
|
+
"smolweb",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 5 - Production/Stable",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Topic :: Software Development :: Libraries",
|
|
28
|
+
"Typing :: Typed",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[[project.authors]]
|
|
32
|
+
name = "Dave Pearson"
|
|
33
|
+
email = "davep@davep.org"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://gophermap.davep.dev/"
|
|
37
|
+
Repository = "https://github.com/davep/gophermap"
|
|
38
|
+
Documentation = "https://gophermap.davep.dev/"
|
|
39
|
+
Source = "https://github.com/davep/gophermap"
|
|
40
|
+
Issues = "https://github.com/davep/gophermap/issues"
|
|
41
|
+
Discussions = "https://github.com/davep/gophermap/discussions"
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
gophermap = "gophermap.__main__:parse_input"
|
|
45
|
+
|
|
46
|
+
[build-system]
|
|
47
|
+
requires = ["uv_build>=0.11.32,<0.12.0"]
|
|
48
|
+
build-backend = "uv_build"
|
|
49
|
+
|
|
50
|
+
[[tool.uv.index]]
|
|
51
|
+
name = "testpypi"
|
|
52
|
+
url = "https://test.pypi.org/simple/"
|
|
53
|
+
publish-url = "https://test.pypi.org/legacy/"
|
|
54
|
+
explicit = true
|
|
55
|
+
|
|
56
|
+
[tool.pyright]
|
|
57
|
+
venvPath = "."
|
|
58
|
+
venv = ".venv"
|
|
59
|
+
exclude = [".venv"]
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
select = [
|
|
63
|
+
"E",
|
|
64
|
+
"F",
|
|
65
|
+
"UP",
|
|
66
|
+
"B",
|
|
67
|
+
"SIM",
|
|
68
|
+
"I",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint.pycodestyle]
|
|
72
|
+
max-line-length = 120
|
|
73
|
+
|
|
74
|
+
[tool.coverage.run]
|
|
75
|
+
omit = ["tests/*"]
|
|
76
|
+
|
|
77
|
+
[tool.coverage.report]
|
|
78
|
+
exclude_lines = [
|
|
79
|
+
"pragma: no cover",
|
|
80
|
+
"def __repr__",
|
|
81
|
+
"raise AssertionError",
|
|
82
|
+
"raise NotImplementedError",
|
|
83
|
+
"if __name__ == .__main__.:",
|
|
84
|
+
"if TYPE_CHECKING:",
|
|
85
|
+
'class .*\bProtocol\):',
|
|
86
|
+
'@(abc\.)?abstractmethod',
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[dependency-groups]
|
|
90
|
+
dev = [
|
|
91
|
+
"codespell>=2.4.3",
|
|
92
|
+
"mypy>=2.3.0",
|
|
93
|
+
"pre-commit>=4.6.0",
|
|
94
|
+
"ruff>=0.15.22",
|
|
95
|
+
]
|
|
96
|
+
docs = [
|
|
97
|
+
"mkdocs>=1.6.1,<2",
|
|
98
|
+
"mkdocs-material>=9.7.6",
|
|
99
|
+
"mkdocstrings[python]>=1.0.4",
|
|
100
|
+
]
|
|
101
|
+
test = [
|
|
102
|
+
"pytest>=9.1.1",
|
|
103
|
+
"pytest-cov>=7.1.0",
|
|
104
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gophermap"
|
|
3
|
-
version = "
|
|
3
|
+
version = "1.1.0"
|
|
4
4
|
description = "A simple library for parsng Gophermaps"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -20,7 +20,7 @@ keywords = [
|
|
|
20
20
|
"smolweb",
|
|
21
21
|
]
|
|
22
22
|
classifiers = [
|
|
23
|
-
"Development Status ::
|
|
23
|
+
"Development Status :: 5 - Production/Stable",
|
|
24
24
|
"Operating System :: OS Independent",
|
|
25
25
|
"Programming Language :: Python :: 3 :: Only",
|
|
26
26
|
"Programming Language :: Python :: 3",
|
|
@@ -16,7 +16,7 @@ __licence__ = "MIT"
|
|
|
16
16
|
|
|
17
17
|
##############################################################################
|
|
18
18
|
# Local imports.
|
|
19
|
-
from .exceptions import GopherMapError
|
|
19
|
+
from .exceptions import GopherMapError
|
|
20
20
|
from .gopher_map import GopherMap
|
|
21
21
|
from .item import GopherItem
|
|
22
22
|
from .item_type import ItemType
|
|
@@ -28,7 +28,6 @@ __all__ = [
|
|
|
28
28
|
"GopherMap",
|
|
29
29
|
"GopherMapError",
|
|
30
30
|
"ItemType",
|
|
31
|
-
"NoFields",
|
|
32
31
|
]
|
|
33
32
|
|
|
34
33
|
### __init__.py ends here
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Provides exceptions for the Gopher map."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
##############################################################################
|
|
5
|
+
class GopherMapError(Exception):
|
|
6
|
+
"""Base class for Gopher map errors."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
##############################################################################
|
|
10
|
+
class EmptyMap(GopherMapError):
|
|
11
|
+
"""Raised when a Gopher map is empty."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##############################################################################
|
|
15
|
+
class NoFields(GopherMapError):
|
|
16
|
+
"""Raised when a Gopher item has no fields."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
##############################################################################
|
|
20
|
+
class UnknownItemType(GopherMapError):
|
|
21
|
+
"""Raised when a Gopher item has an unknown type."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### exceptions.py ends here
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Provides a class for parsing and holding a Gopher map."""
|
|
2
|
+
|
|
3
|
+
##############################################################################
|
|
4
|
+
# Python imports.
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
from typing import Final
|
|
8
|
+
|
|
9
|
+
##############################################################################
|
|
10
|
+
# Local imports.
|
|
11
|
+
from .exceptions import EmptyMap, GopherMapError
|
|
12
|
+
from .item import GopherItem
|
|
13
|
+
from .item_type import ItemType
|
|
14
|
+
|
|
15
|
+
##############################################################################
|
|
16
|
+
EOF: Final[str] = "."
|
|
17
|
+
"""The EOF marker for a Gopher map."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
##############################################################################
|
|
21
|
+
class GopherMap:
|
|
22
|
+
"""A class for parsing and holding a Gopher map."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, map_text: str, strict: bool = False) -> None:
|
|
25
|
+
"""Initialise the Gopher map.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
map_text: The text of the Gopher map.
|
|
29
|
+
strict: Whether to be strict about parsing the Gopher map.
|
|
30
|
+
"""
|
|
31
|
+
self._raw = map_text
|
|
32
|
+
"""The raw text of the Gopher map."""
|
|
33
|
+
self._strict = strict
|
|
34
|
+
"""Whether to be strict about parsing the Gopher map."""
|
|
35
|
+
|
|
36
|
+
def _parse_map(self, map_text: str) -> Iterator[GopherItem]:
|
|
37
|
+
"""Parse the Gopher map text into a list of Gopher items.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
map_text: The text of the Gopher map.
|
|
41
|
+
|
|
42
|
+
Yields:
|
|
43
|
+
Gopher items.
|
|
44
|
+
"""
|
|
45
|
+
if self._strict and not map_text:
|
|
46
|
+
raise EmptyMap("Gopher map is empty")
|
|
47
|
+
for line in map_text.splitlines():
|
|
48
|
+
if line == EOF:
|
|
49
|
+
break
|
|
50
|
+
yield GopherItem(line, self._strict)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def raw(self) -> str:
|
|
54
|
+
"""The raw text of the Gopher map."""
|
|
55
|
+
return self._raw
|
|
56
|
+
|
|
57
|
+
@cached_property
|
|
58
|
+
def items(self) -> tuple[GopherItem, ...]:
|
|
59
|
+
"""The list of Gopher items in the map.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
EmptyMap: If the map is empty and strict mode is enabled.
|
|
63
|
+
NoFields: If the line is missing a tab character and strict mode is enabled.
|
|
64
|
+
UnknownItemType: If the item type is unknown and strict mode is enabled.
|
|
65
|
+
"""
|
|
66
|
+
return tuple(self._parse_map(self._raw))
|
|
67
|
+
|
|
68
|
+
@cached_property
|
|
69
|
+
def has_error(self) -> bool:
|
|
70
|
+
"""Whether the Gopher map contains an error item.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
EmptyMap: If the map is empty and strict mode is enabled.
|
|
74
|
+
NoFields: If the line is missing a tab character and strict mode is enabled.
|
|
75
|
+
UnknownItemType: If the item type is unknown and strict mode is enabled.
|
|
76
|
+
|
|
77
|
+
Note:
|
|
78
|
+
The mentioned exceptions will be raised on accessing this
|
|
79
|
+
property, if in strict mode. If you wish to check for errors
|
|
80
|
+
without raising exceptions, use the [`is_likely_error` class
|
|
81
|
+
method][gophermap.GopherMap.is_likely_error], to test instead.
|
|
82
|
+
"""
|
|
83
|
+
return any(item.type is ItemType.ERROR for item in self.items)
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def is_likely_a_map(cls, text: str) -> bool:
|
|
87
|
+
"""Determine if the text is likely a valid Gopher map.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
text: The text of the Gopher map.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
True if the text is likely a valid Gopher map, False otherwise.
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
_ = cls(text, strict=True).items
|
|
97
|
+
except GopherMapError:
|
|
98
|
+
return False
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def is_likely_error(cls, text: str) -> bool:
|
|
103
|
+
"""Determine if the text is likely a Gopher error message.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
text: The text to test.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
True if the text is likely a Gopher error message, False otherwise.
|
|
110
|
+
"""
|
|
111
|
+
try:
|
|
112
|
+
return cls(text, strict=True).has_error
|
|
113
|
+
except GopherMapError:
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### gopher_map.py ends here
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
##############################################################################
|
|
4
4
|
# Local imports.
|
|
5
|
+
from .exceptions import NoFields, UnknownItemType
|
|
5
6
|
from .item_type import ItemType
|
|
6
7
|
|
|
7
8
|
|
|
@@ -9,11 +10,16 @@ from .item_type import ItemType
|
|
|
9
10
|
class GopherItem:
|
|
10
11
|
"""A class for holding an item in the Gopher map."""
|
|
11
12
|
|
|
12
|
-
def __init__(self, line: str) -> None:
|
|
13
|
+
def __init__(self, line: str, strict: bool = False) -> None:
|
|
13
14
|
"""Initialise the Gopher item.
|
|
14
15
|
|
|
15
16
|
Args:
|
|
16
17
|
line: The line of text from the Gopher map.
|
|
18
|
+
strict: Whether to be strict about parsing the Gopher item.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
NoFields: If the line is missing a tab character and strict mode is enabled.
|
|
22
|
+
UnknownItemType: If the item type is unknown and strict mode is enabled.
|
|
17
23
|
"""
|
|
18
24
|
self._raw = line
|
|
19
25
|
"""The raw text of the Gopher item."""
|
|
@@ -29,6 +35,12 @@ class GopherItem:
|
|
|
29
35
|
"""The host of the Gopher item."""
|
|
30
36
|
self._port = int(fields[3]) if len(fields) > 3 and fields[3].isdigit() else 70
|
|
31
37
|
"""The port of the Gopher item."""
|
|
38
|
+
# If we're in strict mode, let's do some harsh checks.
|
|
39
|
+
if strict:
|
|
40
|
+
if "\t" not in line:
|
|
41
|
+
raise NoFields(f"Line is missing a tab character: {line!r}")
|
|
42
|
+
if self._type is ItemType.UNKNOWN:
|
|
43
|
+
raise UnknownItemType(f"Unknown item type: {self._type!r}")
|
|
32
44
|
|
|
33
45
|
@property
|
|
34
46
|
def raw(self) -> str:
|
|
@@ -45,8 +45,12 @@ class ItemType(StrEnum):
|
|
|
45
45
|
"""A document file."""
|
|
46
46
|
AUDIO = "s"
|
|
47
47
|
"""An audio file."""
|
|
48
|
+
PNG = "p"
|
|
49
|
+
"""A PNG image."""
|
|
48
50
|
PDF = "P"
|
|
49
51
|
"""A PDF file."""
|
|
52
|
+
RTF = "r"
|
|
53
|
+
"""A rich text format file."""
|
|
50
54
|
XML = "X"
|
|
51
55
|
"""An XML file."""
|
|
52
56
|
UNKNOWN = "unknown"
|
|
@@ -92,6 +96,8 @@ class ItemType(StrEnum):
|
|
|
92
96
|
ItemType.AUDIO: "application/octet-stream",
|
|
93
97
|
ItemType.PDF: "application/pdf",
|
|
94
98
|
ItemType.XML: "application/xml",
|
|
99
|
+
ItemType.PNG: "image/png",
|
|
100
|
+
ItemType.RTF: "application/rtf",
|
|
95
101
|
}.get(self, "application/x-gopher-unknown")
|
|
96
102
|
|
|
97
103
|
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""Exceptions for the library.
|
|
2
|
-
|
|
3
|
-
Note:
|
|
4
|
-
This module is now deprecated and will be removed in a future release.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
##############################################################################
|
|
9
|
-
class GopherMapError(Exception):
|
|
10
|
-
"""Base exception for all errors raised by the GopherMap library.
|
|
11
|
-
|
|
12
|
-
Note:
|
|
13
|
-
This exception class is now deprecated and will be removed in a
|
|
14
|
-
future release.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
##############################################################################
|
|
19
|
-
class NoFields(GopherMapError):
|
|
20
|
-
"""Raised when a Gopher item has no fields.
|
|
21
|
-
|
|
22
|
-
Note:
|
|
23
|
-
This exception class is now deprecated and will be removed in a
|
|
24
|
-
future release.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
### exceptions.py ends here
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"""Provides a class for parsing and holding a Gopher map."""
|
|
2
|
-
|
|
3
|
-
##############################################################################
|
|
4
|
-
# Python imports.
|
|
5
|
-
from collections.abc import Iterator
|
|
6
|
-
from functools import cached_property
|
|
7
|
-
from typing import Final
|
|
8
|
-
|
|
9
|
-
##############################################################################
|
|
10
|
-
# Local imports.
|
|
11
|
-
from .item import GopherItem
|
|
12
|
-
|
|
13
|
-
##############################################################################
|
|
14
|
-
EOF: Final[str] = "."
|
|
15
|
-
"""The EOF marker for a Gopher map."""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
##############################################################################
|
|
19
|
-
class GopherMap:
|
|
20
|
-
"""A class for parsing and holding a Gopher map."""
|
|
21
|
-
|
|
22
|
-
def __init__(self, map_text: str) -> None:
|
|
23
|
-
"""Initialise the Gopher map.
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
map_text: The text of the Gopher map.
|
|
27
|
-
"""
|
|
28
|
-
self._raw = map_text
|
|
29
|
-
"""The raw text of the Gopher map."""
|
|
30
|
-
|
|
31
|
-
@staticmethod
|
|
32
|
-
def _parse_map(map_text: str) -> Iterator[GopherItem]:
|
|
33
|
-
"""Parse the Gopher map text into a list of Gopher items.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
map_text: The text of the Gopher map.
|
|
37
|
-
|
|
38
|
-
Yields:
|
|
39
|
-
Gopher items.
|
|
40
|
-
"""
|
|
41
|
-
for line in map_text.splitlines():
|
|
42
|
-
if line == EOF:
|
|
43
|
-
break
|
|
44
|
-
yield GopherItem(line)
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def raw(self) -> str:
|
|
48
|
-
"""The raw text of the Gopher map."""
|
|
49
|
-
return self._raw
|
|
50
|
-
|
|
51
|
-
@cached_property
|
|
52
|
-
def items(self) -> tuple[GopherItem, ...]:
|
|
53
|
-
"""The list of Gopher items in the map."""
|
|
54
|
-
return tuple(self._parse_map(self._raw))
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
### gopher_map.py ends here
|
|
File without changes
|
|
File without changes
|
|
File without changes
|