ghps 0.2__tar.gz → 0.3__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.
- {ghps-0.2 → ghps-0.3}/CHANGELOG.md +11 -1
- {ghps-0.2 → ghps-0.3}/PKG-INFO +22 -5
- {ghps-0.2 → ghps-0.3}/README.md +9 -2
- {ghps-0.2 → ghps-0.3}/SECURITY.md +2 -2
- {ghps-0.2 → ghps-0.3}/ghps/cli.py +35 -20
- {ghps-0.2 → ghps-0.3}/ghps/params.py +2 -1
- {ghps-0.2 → ghps-0.3}/ghps/server.py +20 -3
- {ghps-0.2 → ghps-0.3}/ghps.egg-info/PKG-INFO +22 -5
- {ghps-0.2 → ghps-0.3}/setup.py +2 -2
- {ghps-0.2 → ghps-0.3}/tests/test_cli.py +14 -0
- {ghps-0.2 → ghps-0.3}/tests/test_error.py +34 -0
- {ghps-0.2 → ghps-0.3}/tests/test_server.py +49 -0
- {ghps-0.2 → ghps-0.3}/AUTHORS.md +0 -0
- {ghps-0.2 → ghps-0.3}/LICENSE +0 -0
- {ghps-0.2 → ghps-0.3}/MANIFEST.in +0 -0
- {ghps-0.2 → ghps-0.3}/dev-requirements.txt +0 -0
- {ghps-0.2 → ghps-0.3}/ghps/__init__.py +0 -0
- {ghps-0.2 → ghps-0.3}/ghps/__main__.py +0 -0
- {ghps-0.2 → ghps-0.3}/ghps.egg-info/SOURCES.txt +0 -0
- {ghps-0.2 → ghps-0.3}/ghps.egg-info/dependency_links.txt +0 -0
- {ghps-0.2 → ghps-0.3}/ghps.egg-info/entry_points.txt +0 -0
- {ghps-0.2 → ghps-0.3}/ghps.egg-info/requires.txt +0 -0
- {ghps-0.2 → ghps-0.3}/ghps.egg-info/top_level.txt +0 -0
- {ghps-0.2 → ghps-0.3}/requirements.txt +0 -0
- {ghps-0.2 → ghps-0.3}/setup.cfg +0 -0
|
@@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
|
+
## [0.3] - 2026-04-05
|
|
9
|
+
### Added
|
|
10
|
+
- `auto_open` parameter
|
|
11
|
+
- `--auto-open` CLI argument
|
|
12
|
+
- Random port support
|
|
13
|
+
### Changed
|
|
14
|
+
- Test system modified
|
|
15
|
+
- CLI error messages updated
|
|
16
|
+
- `README.md` updated
|
|
8
17
|
## [0.2] - 2026-03-19
|
|
9
18
|
### Added
|
|
10
19
|
- Logo
|
|
@@ -18,7 +27,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
18
27
|
- Command line interface
|
|
19
28
|
- Test system
|
|
20
29
|
|
|
21
|
-
[Unreleased]: https://github.com/sepandhaghighi/ghps/compare/v0.
|
|
30
|
+
[Unreleased]: https://github.com/sepandhaghighi/ghps/compare/v0.3...dev
|
|
31
|
+
[0.3]: https://github.com/sepandhaghighi/ghps/compare/v0.2...v0.3
|
|
22
32
|
[0.2]: https://github.com/sepandhaghighi/ghps/compare/v0.1...v0.2
|
|
23
33
|
[0.1]: https://github.com/sepandhaghighi/ghps/compare/bc0a425...v0.1
|
|
24
34
|
|
{ghps-0.2 → ghps-0.3}/PKG-INFO
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghps
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3
|
|
4
4
|
Summary: Ghps: A Minimal GitHub Pages Simulator for Local Development
|
|
5
5
|
Home-page: https://github.com/sepandhaghighi/ghps
|
|
6
|
-
Download-URL: https://github.com/sepandhaghighi/ghps/tarball/v0.
|
|
6
|
+
Download-URL: https://github.com/sepandhaghighi/ghps/tarball/v0.3
|
|
7
7
|
Author: Sepand Haghighi
|
|
8
8
|
Author-email: me@sepand.tech
|
|
9
9
|
License: MIT
|
|
@@ -101,13 +101,13 @@ Dynamic: summary
|
|
|
101
101
|
## Installation
|
|
102
102
|
|
|
103
103
|
### Source Code
|
|
104
|
-
- Download [Version 0.
|
|
104
|
+
- Download [Version 0.3](https://github.com/sepandhaghighi/ghps/archive/v0.3.zip) or [Latest Source](https://github.com/sepandhaghighi/ghps/archive/dev.zip)
|
|
105
105
|
- `pip install .`
|
|
106
106
|
|
|
107
107
|
### PyPI
|
|
108
108
|
|
|
109
109
|
- Check [Python Packaging User Guide](https://packaging.python.org/installing/)
|
|
110
|
-
- `pip install ghps==0.
|
|
110
|
+
- `pip install ghps==0.3`
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
## Usage
|
|
@@ -130,6 +130,9 @@ ghps --port=8080 --no-cache --no-threaded
|
|
|
130
130
|
| `--no-strict` | Enable `.html` fallback| Strict mode enabled |
|
|
131
131
|
| `--no-cache` | Disable HTTP caching headers | Cache enabled |
|
|
132
132
|
| `--no-threaded` | Disable threaded server | Threaded enabled |
|
|
133
|
+
| `--auto-open` | Automatically open the server URL | Disable |
|
|
134
|
+
|
|
135
|
+
ℹ️ You can set `--port=0` to let the OS automatically select an available port
|
|
133
136
|
|
|
134
137
|
|
|
135
138
|
### Library
|
|
@@ -144,6 +147,7 @@ server = GHPageServer(
|
|
|
144
147
|
strict=True,
|
|
145
148
|
no_cache=False,
|
|
146
149
|
threaded=True,
|
|
150
|
+
auto_open=True,
|
|
147
151
|
)
|
|
148
152
|
|
|
149
153
|
server.start()
|
|
@@ -159,6 +163,9 @@ server.start()
|
|
|
159
163
|
| `strict` | `bool` | Enforce GitHub-like routing behavior | `True` |
|
|
160
164
|
| `no_cache` | `bool` | Disable cache headers | `False` |
|
|
161
165
|
| `threaded` | `bool` | Enable threaded request handling | `True` |
|
|
166
|
+
| `auto_open` | `bool` | Automatically open the server URL | `False` |
|
|
167
|
+
|
|
168
|
+
ℹ️ You can set `port=0` to let the OS automatically select an available port
|
|
162
169
|
|
|
163
170
|
## Issues & Bug Reports
|
|
164
171
|
|
|
@@ -209,6 +216,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
|
209
216
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
210
217
|
|
|
211
218
|
## [Unreleased]
|
|
219
|
+
## [0.3] - 2026-04-05
|
|
220
|
+
### Added
|
|
221
|
+
- `auto_open` parameter
|
|
222
|
+
- `--auto-open` CLI argument
|
|
223
|
+
- Random port support
|
|
224
|
+
### Changed
|
|
225
|
+
- Test system modified
|
|
226
|
+
- CLI error messages updated
|
|
227
|
+
- `README.md` updated
|
|
212
228
|
## [0.2] - 2026-03-19
|
|
213
229
|
### Added
|
|
214
230
|
- Logo
|
|
@@ -222,7 +238,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
222
238
|
- Command line interface
|
|
223
239
|
- Test system
|
|
224
240
|
|
|
225
|
-
[Unreleased]: https://github.com/sepandhaghighi/ghps/compare/v0.
|
|
241
|
+
[Unreleased]: https://github.com/sepandhaghighi/ghps/compare/v0.3...dev
|
|
242
|
+
[0.3]: https://github.com/sepandhaghighi/ghps/compare/v0.2...v0.3
|
|
226
243
|
[0.2]: https://github.com/sepandhaghighi/ghps/compare/v0.1...v0.2
|
|
227
244
|
[0.1]: https://github.com/sepandhaghighi/ghps/compare/bc0a425...v0.1
|
|
228
245
|
|
{ghps-0.2 → ghps-0.3}/README.md
RENAMED
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
## Installation
|
|
51
51
|
|
|
52
52
|
### Source Code
|
|
53
|
-
- Download [Version 0.
|
|
53
|
+
- Download [Version 0.3](https://github.com/sepandhaghighi/ghps/archive/v0.3.zip) or [Latest Source](https://github.com/sepandhaghighi/ghps/archive/dev.zip)
|
|
54
54
|
- `pip install .`
|
|
55
55
|
|
|
56
56
|
### PyPI
|
|
57
57
|
|
|
58
58
|
- Check [Python Packaging User Guide](https://packaging.python.org/installing/)
|
|
59
|
-
- `pip install ghps==0.
|
|
59
|
+
- `pip install ghps==0.3`
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
## Usage
|
|
@@ -79,6 +79,9 @@ ghps --port=8080 --no-cache --no-threaded
|
|
|
79
79
|
| `--no-strict` | Enable `.html` fallback| Strict mode enabled |
|
|
80
80
|
| `--no-cache` | Disable HTTP caching headers | Cache enabled |
|
|
81
81
|
| `--no-threaded` | Disable threaded server | Threaded enabled |
|
|
82
|
+
| `--auto-open` | Automatically open the server URL | Disable |
|
|
83
|
+
|
|
84
|
+
ℹ️ You can set `--port=0` to let the OS automatically select an available port
|
|
82
85
|
|
|
83
86
|
|
|
84
87
|
### Library
|
|
@@ -93,6 +96,7 @@ server = GHPageServer(
|
|
|
93
96
|
strict=True,
|
|
94
97
|
no_cache=False,
|
|
95
98
|
threaded=True,
|
|
99
|
+
auto_open=True,
|
|
96
100
|
)
|
|
97
101
|
|
|
98
102
|
server.start()
|
|
@@ -108,6 +112,9 @@ server.start()
|
|
|
108
112
|
| `strict` | `bool` | Enforce GitHub-like routing behavior | `True` |
|
|
109
113
|
| `no_cache` | `bool` | Disable cache headers | `False` |
|
|
110
114
|
| `threaded` | `bool` | Enable threaded request handling | `True` |
|
|
115
|
+
| `auto_open` | `bool` | Automatically open the server URL | `False` |
|
|
116
|
+
|
|
117
|
+
ℹ️ You can set `port=0` to let the OS automatically select an available port
|
|
111
118
|
|
|
112
119
|
## Issues & Bug Reports
|
|
113
120
|
|
|
@@ -7,6 +7,16 @@ from pathlib import Path
|
|
|
7
7
|
from .params import GHPS_VERSION
|
|
8
8
|
from .server import GHPageServer
|
|
9
9
|
|
|
10
|
+
def _print_cli_error(message: str, exit_code: int = 1) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Print a formatted CLI error message and exit.
|
|
13
|
+
|
|
14
|
+
:param message: Error message to display.
|
|
15
|
+
:param exit_code: Exit status code (default: 1).
|
|
16
|
+
"""
|
|
17
|
+
print(f"[GHPS ERROR] {message}", file=sys.stderr)
|
|
18
|
+
sys.exit(exit_code)
|
|
19
|
+
|
|
10
20
|
|
|
11
21
|
def _parse_args() -> argparse.Namespace:
|
|
12
22
|
"""Parse arguments."""
|
|
@@ -57,29 +67,34 @@ def _parse_args() -> argparse.Namespace:
|
|
|
57
67
|
help="Disable threaded server"
|
|
58
68
|
)
|
|
59
69
|
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--auto-open",
|
|
72
|
+
action="store_true",
|
|
73
|
+
help="Automatically open the server URL"
|
|
74
|
+
)
|
|
75
|
+
|
|
60
76
|
args = parser.parse_args()
|
|
61
77
|
return args
|
|
62
78
|
|
|
63
79
|
|
|
64
80
|
def main() -> None:
|
|
65
81
|
"""CLI main function."""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
server.start()
|
|
82
|
+
try:
|
|
83
|
+
args = _parse_args()
|
|
84
|
+
if args.version:
|
|
85
|
+
print(GHPS_VERSION)
|
|
86
|
+
else:
|
|
87
|
+
server = GHPageServer(
|
|
88
|
+
directory=args.directory,
|
|
89
|
+
port=args.port,
|
|
90
|
+
base_path=args.base_path,
|
|
91
|
+
strict=not args.no_strict,
|
|
92
|
+
no_cache=args.no_cache,
|
|
93
|
+
threaded=not args.no_threaded,
|
|
94
|
+
auto_open=args.auto_open,
|
|
95
|
+
)
|
|
96
|
+
server.start()
|
|
97
|
+
except ValueError as e:
|
|
98
|
+
_print_cli_error(str(e))
|
|
99
|
+
except Exception as e:
|
|
100
|
+
_print_cli_error(f"Unexpected error: {e}")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""ghps params."""
|
|
3
3
|
|
|
4
|
-
GHPS_VERSION = "0.
|
|
4
|
+
GHPS_VERSION = "0.3"
|
|
5
5
|
|
|
6
6
|
INVALID_DIRECTORY_TYPE_ERROR = "`directory` must be str or pathlib.Path."
|
|
7
7
|
DIRECTORY_NOT_FOUND_ERROR = "`directory` does not exist."
|
|
@@ -16,3 +16,4 @@ INVALID_BASE_PATH_FORMAT_ERROR = "`base_path` must start with '/' or be empty."
|
|
|
16
16
|
INVALID_STRICT_TYPE_ERROR = "`strict` must be bool."
|
|
17
17
|
INVALID_NO_CACHE_TYPE_ERROR = "`no_cache` must be bool."
|
|
18
18
|
INVALID_THREADED_TYPE_ERROR = "`threaded` must be bool."
|
|
19
|
+
INVALID_AUTO_OPEN_TYPE_ERROR = "`auto_open` must be bool."
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import http.server
|
|
5
5
|
import socketserver
|
|
6
|
+
import webbrowser
|
|
6
7
|
from typing import Optional, Any
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from urllib.parse import unquote
|
|
@@ -17,6 +18,7 @@ from .params import (
|
|
|
17
18
|
INVALID_STRICT_TYPE_ERROR,
|
|
18
19
|
INVALID_NO_CACHE_TYPE_ERROR,
|
|
19
20
|
INVALID_THREADED_TYPE_ERROR,
|
|
21
|
+
INVALID_AUTO_OPEN_TYPE_ERROR
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
|
|
@@ -27,6 +29,7 @@ def _validate_inputs(
|
|
|
27
29
|
strict: Any,
|
|
28
30
|
no_cache: Any,
|
|
29
31
|
threaded: Any,
|
|
32
|
+
auto_open: Any,
|
|
30
33
|
):
|
|
31
34
|
"""
|
|
32
35
|
Validate GHPageServer inputs.
|
|
@@ -37,6 +40,7 @@ def _validate_inputs(
|
|
|
37
40
|
:param strict: If False, enables automatic ".html" resolution.
|
|
38
41
|
:param no_cache: If True, disables client-side caching.
|
|
39
42
|
:param threaded: If True, handles requests using threads.
|
|
43
|
+
:param auto_open: If True, automatically opens the server URL in the default web browser.
|
|
40
44
|
"""
|
|
41
45
|
if not isinstance(directory, (str, Path)):
|
|
42
46
|
raise ValueError(INVALID_DIRECTORY_TYPE_ERROR)
|
|
@@ -51,7 +55,7 @@ def _validate_inputs(
|
|
|
51
55
|
if not isinstance(port, int):
|
|
52
56
|
raise ValueError(INVALID_PORT_TYPE_ERROR)
|
|
53
57
|
|
|
54
|
-
if not (
|
|
58
|
+
if not (0 <= port <= 65535):
|
|
55
59
|
raise ValueError(INVALID_PORT_RANGE_ERROR)
|
|
56
60
|
|
|
57
61
|
if not isinstance(base_path, str):
|
|
@@ -69,6 +73,9 @@ def _validate_inputs(
|
|
|
69
73
|
if not isinstance(threaded, bool):
|
|
70
74
|
raise ValueError(INVALID_THREADED_TYPE_ERROR)
|
|
71
75
|
|
|
76
|
+
if not isinstance(auto_open, bool):
|
|
77
|
+
raise ValueError(INVALID_AUTO_OPEN_TYPE_ERROR)
|
|
78
|
+
|
|
72
79
|
|
|
73
80
|
class _GHRequestHandler(http.server.SimpleHTTPRequestHandler):
|
|
74
81
|
"""Custom HTTP request handler for serving static files with optional base path stripping, strict routing, custom 404 page support, and cache control headers."""
|
|
@@ -178,6 +185,7 @@ class GHPageServer:
|
|
|
178
185
|
strict: bool = True,
|
|
179
186
|
no_cache: bool = False,
|
|
180
187
|
threaded: bool = True,
|
|
188
|
+
auto_open: bool = False,
|
|
181
189
|
):
|
|
182
190
|
"""
|
|
183
191
|
Initialize the server.
|
|
@@ -188,6 +196,7 @@ class GHPageServer:
|
|
|
188
196
|
:param strict: If False, enables automatic ".html" resolution.
|
|
189
197
|
:param no_cache: If True, disables client-side caching.
|
|
190
198
|
:param threaded: If True, handles requests using threads.
|
|
199
|
+
:param auto_open: If True, automatically opens the server URL in the default web browser.
|
|
191
200
|
"""
|
|
192
201
|
_validate_inputs(
|
|
193
202
|
directory=directory,
|
|
@@ -196,6 +205,7 @@ class GHPageServer:
|
|
|
196
205
|
strict=strict,
|
|
197
206
|
no_cache=no_cache,
|
|
198
207
|
threaded=threaded,
|
|
208
|
+
auto_open=auto_open,
|
|
199
209
|
)
|
|
200
210
|
self._directory = str(Path(directory).resolve())
|
|
201
211
|
self._port = port
|
|
@@ -203,6 +213,7 @@ class GHPageServer:
|
|
|
203
213
|
self._strict = strict
|
|
204
214
|
self._no_cache = no_cache
|
|
205
215
|
self._threaded = threaded
|
|
216
|
+
self._auto_open = auto_open
|
|
206
217
|
self._httpd = None
|
|
207
218
|
|
|
208
219
|
def start(self) -> None:
|
|
@@ -223,12 +234,18 @@ class GHPageServer:
|
|
|
223
234
|
server_cls = _ThreadedTCPServer if self._threaded else socketserver.TCPServer
|
|
224
235
|
|
|
225
236
|
self._httpd = server_cls(("", self._port), handler)
|
|
226
|
-
|
|
227
|
-
|
|
237
|
+
self._port = self._httpd.server_address[1]
|
|
238
|
+
url = f"http://localhost:{self._port}{self._base_path}"
|
|
239
|
+
print(f"Serving at {url}")
|
|
228
240
|
print(f"Directory: {self._directory}")
|
|
229
241
|
print(f"Strict mode: {'ON' if self._strict else 'OFF'}")
|
|
230
242
|
print(f"Cache disabled: {'YES' if self._no_cache else 'NO'}")
|
|
231
243
|
|
|
244
|
+
if self._auto_open:
|
|
245
|
+
try:
|
|
246
|
+
webbrowser.open(url)
|
|
247
|
+
except Exception:
|
|
248
|
+
print("[GHPS ERROR]: Failed to open browser automatically")
|
|
232
249
|
try:
|
|
233
250
|
self._httpd.serve_forever()
|
|
234
251
|
except KeyboardInterrupt:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghps
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3
|
|
4
4
|
Summary: Ghps: A Minimal GitHub Pages Simulator for Local Development
|
|
5
5
|
Home-page: https://github.com/sepandhaghighi/ghps
|
|
6
|
-
Download-URL: https://github.com/sepandhaghighi/ghps/tarball/v0.
|
|
6
|
+
Download-URL: https://github.com/sepandhaghighi/ghps/tarball/v0.3
|
|
7
7
|
Author: Sepand Haghighi
|
|
8
8
|
Author-email: me@sepand.tech
|
|
9
9
|
License: MIT
|
|
@@ -101,13 +101,13 @@ Dynamic: summary
|
|
|
101
101
|
## Installation
|
|
102
102
|
|
|
103
103
|
### Source Code
|
|
104
|
-
- Download [Version 0.
|
|
104
|
+
- Download [Version 0.3](https://github.com/sepandhaghighi/ghps/archive/v0.3.zip) or [Latest Source](https://github.com/sepandhaghighi/ghps/archive/dev.zip)
|
|
105
105
|
- `pip install .`
|
|
106
106
|
|
|
107
107
|
### PyPI
|
|
108
108
|
|
|
109
109
|
- Check [Python Packaging User Guide](https://packaging.python.org/installing/)
|
|
110
|
-
- `pip install ghps==0.
|
|
110
|
+
- `pip install ghps==0.3`
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
## Usage
|
|
@@ -130,6 +130,9 @@ ghps --port=8080 --no-cache --no-threaded
|
|
|
130
130
|
| `--no-strict` | Enable `.html` fallback| Strict mode enabled |
|
|
131
131
|
| `--no-cache` | Disable HTTP caching headers | Cache enabled |
|
|
132
132
|
| `--no-threaded` | Disable threaded server | Threaded enabled |
|
|
133
|
+
| `--auto-open` | Automatically open the server URL | Disable |
|
|
134
|
+
|
|
135
|
+
ℹ️ You can set `--port=0` to let the OS automatically select an available port
|
|
133
136
|
|
|
134
137
|
|
|
135
138
|
### Library
|
|
@@ -144,6 +147,7 @@ server = GHPageServer(
|
|
|
144
147
|
strict=True,
|
|
145
148
|
no_cache=False,
|
|
146
149
|
threaded=True,
|
|
150
|
+
auto_open=True,
|
|
147
151
|
)
|
|
148
152
|
|
|
149
153
|
server.start()
|
|
@@ -159,6 +163,9 @@ server.start()
|
|
|
159
163
|
| `strict` | `bool` | Enforce GitHub-like routing behavior | `True` |
|
|
160
164
|
| `no_cache` | `bool` | Disable cache headers | `False` |
|
|
161
165
|
| `threaded` | `bool` | Enable threaded request handling | `True` |
|
|
166
|
+
| `auto_open` | `bool` | Automatically open the server URL | `False` |
|
|
167
|
+
|
|
168
|
+
ℹ️ You can set `port=0` to let the OS automatically select an available port
|
|
162
169
|
|
|
163
170
|
## Issues & Bug Reports
|
|
164
171
|
|
|
@@ -209,6 +216,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
|
209
216
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
210
217
|
|
|
211
218
|
## [Unreleased]
|
|
219
|
+
## [0.3] - 2026-04-05
|
|
220
|
+
### Added
|
|
221
|
+
- `auto_open` parameter
|
|
222
|
+
- `--auto-open` CLI argument
|
|
223
|
+
- Random port support
|
|
224
|
+
### Changed
|
|
225
|
+
- Test system modified
|
|
226
|
+
- CLI error messages updated
|
|
227
|
+
- `README.md` updated
|
|
212
228
|
## [0.2] - 2026-03-19
|
|
213
229
|
### Added
|
|
214
230
|
- Logo
|
|
@@ -222,7 +238,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
222
238
|
- Command line interface
|
|
223
239
|
- Test system
|
|
224
240
|
|
|
225
|
-
[Unreleased]: https://github.com/sepandhaghighi/ghps/compare/v0.
|
|
241
|
+
[Unreleased]: https://github.com/sepandhaghighi/ghps/compare/v0.3...dev
|
|
242
|
+
[0.3]: https://github.com/sepandhaghighi/ghps/compare/v0.2...v0.3
|
|
226
243
|
[0.2]: https://github.com/sepandhaghighi/ghps/compare/v0.1...v0.2
|
|
227
244
|
[0.1]: https://github.com/sepandhaghighi/ghps/compare/bc0a425...v0.1
|
|
228
245
|
|
{ghps-0.2 → ghps-0.3}/setup.py
RENAMED
|
@@ -32,7 +32,7 @@ def read_description() -> str:
|
|
|
32
32
|
setup(
|
|
33
33
|
name='ghps',
|
|
34
34
|
packages=['ghps'],
|
|
35
|
-
version='0.
|
|
35
|
+
version='0.3',
|
|
36
36
|
description='Ghps: A Minimal GitHub Pages Simulator for Local Development',
|
|
37
37
|
long_description=read_description(),
|
|
38
38
|
long_description_content_type='text/markdown',
|
|
@@ -40,7 +40,7 @@ setup(
|
|
|
40
40
|
author='Sepand Haghighi',
|
|
41
41
|
author_email='me@sepand.tech',
|
|
42
42
|
url='https://github.com/sepandhaghighi/ghps',
|
|
43
|
-
download_url='https://github.com/sepandhaghighi/ghps/tarball/v0.
|
|
43
|
+
download_url='https://github.com/sepandhaghighi/ghps/tarball/v0.3',
|
|
44
44
|
keywords="github gh-pages static-server static-hosting development server local http simulator testing cli python",
|
|
45
45
|
project_urls={
|
|
46
46
|
'Source': 'https://github.com/sepandhaghighi/ghps'
|
|
@@ -110,3 +110,17 @@ def test_cli_invalid_directory(monkeypatch):
|
|
|
110
110
|
|
|
111
111
|
with pytest.raises(SystemExit):
|
|
112
112
|
ghps.cli.main()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@patch("ghps.cli.GHPageServer")
|
|
116
|
+
@patch("sys.stderr")
|
|
117
|
+
def test_cli_unexpected_error(mock_stderr, mock_server_cls):
|
|
118
|
+
mock_server = mock_server_cls.return_value
|
|
119
|
+
mock_server.start.side_effect = Exception("boom")
|
|
120
|
+
test_args = ["ghps"]
|
|
121
|
+
with patch("sys.argv", test_args):
|
|
122
|
+
with pytest.raises(SystemExit) as exc:
|
|
123
|
+
ghps.cli.main()
|
|
124
|
+
assert exc.value.code == 1
|
|
125
|
+
written = "".join(call.args[0] for call in mock_stderr.write.call_args_list)
|
|
126
|
+
assert "[GHPS ERROR] Unexpected error: boom" in written
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import pytest
|
|
3
|
+
from unittest.mock import patch
|
|
3
4
|
from ghps import GHPageServer
|
|
4
5
|
|
|
5
6
|
|
|
@@ -124,3 +125,36 @@ def test_invalid_threaded_type(tmp_path):
|
|
|
124
125
|
no_cache=False,
|
|
125
126
|
threaded="true",
|
|
126
127
|
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_invalid_auto_open_type(tmp_path):
|
|
131
|
+
with pytest.raises(ValueError, match="`auto_open` must be bool."):
|
|
132
|
+
GHPageServer(
|
|
133
|
+
directory=tmp_path,
|
|
134
|
+
port=8000,
|
|
135
|
+
base_path="",
|
|
136
|
+
strict=True,
|
|
137
|
+
no_cache=False,
|
|
138
|
+
threaded=True,
|
|
139
|
+
auto_open="true",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@patch("ghps.server.webbrowser.open", side_effect=Exception("boom"))
|
|
144
|
+
@patch("ghps.server._ThreadedTCPServer")
|
|
145
|
+
@patch("builtins.print")
|
|
146
|
+
def test_auto_open_failure_logs_error(mock_print, mock_server_cls, mock_web_open, tmp_path):
|
|
147
|
+
mock_server = mock_server_cls.return_value
|
|
148
|
+
mock_server.server_address = ("127.0.0.1", 8000)
|
|
149
|
+
mock_server.serve_forever.side_effect = KeyboardInterrupt
|
|
150
|
+
server = GHPageServer(
|
|
151
|
+
directory=tmp_path,
|
|
152
|
+
port=8000,
|
|
153
|
+
auto_open=True,
|
|
154
|
+
)
|
|
155
|
+
server.start()
|
|
156
|
+
mock_web_open.assert_called_once_with("http://localhost:8000")
|
|
157
|
+
assert any(
|
|
158
|
+
"[GHPS ERROR]: Failed to open browser automatically" in str(call)
|
|
159
|
+
for call in mock_print.call_args_list
|
|
160
|
+
)
|
|
@@ -128,3 +128,52 @@ def test_keyboard_interrupt(capsys):
|
|
|
128
128
|
|
|
129
129
|
captured = capsys.readouterr()
|
|
130
130
|
assert "Server stopped." in captured.out
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@patch("ghps.server.webbrowser.open")
|
|
134
|
+
@patch("ghps.server._ThreadedTCPServer")
|
|
135
|
+
def test_auto_open_enabled(mock_server_cls, mock_web_open, tmp_path):
|
|
136
|
+
mock_server = mock_server_cls.return_value
|
|
137
|
+
mock_server.server_address = ("127.0.0.1", 8000)
|
|
138
|
+
mock_server.serve_forever.side_effect = KeyboardInterrupt
|
|
139
|
+
|
|
140
|
+
server = GHPageServer(
|
|
141
|
+
directory=tmp_path,
|
|
142
|
+
port=8000,
|
|
143
|
+
auto_open=True,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
server.start()
|
|
147
|
+
|
|
148
|
+
mock_web_open.assert_called_once_with("http://localhost:8000")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@patch("ghps.server.webbrowser.open")
|
|
152
|
+
@patch("ghps.server._ThreadedTCPServer")
|
|
153
|
+
def test_auto_open_disabled(mock_server_cls, mock_web_open, tmp_path):
|
|
154
|
+
mock_server = mock_server_cls.return_value
|
|
155
|
+
mock_server.server_address = ("127.0.0.1", 8000)
|
|
156
|
+
mock_server.serve_forever.side_effect = KeyboardInterrupt
|
|
157
|
+
|
|
158
|
+
server = GHPageServer(
|
|
159
|
+
directory=tmp_path,
|
|
160
|
+
port=8000,
|
|
161
|
+
auto_open=False,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
server.start()
|
|
165
|
+
|
|
166
|
+
mock_web_open.assert_not_called()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@patch("ghps.server._ThreadedTCPServer")
|
|
170
|
+
def test_random_port_resolution(mock_server_cls, tmp_path):
|
|
171
|
+
mock_server = mock_server_cls.return_value
|
|
172
|
+
mock_server.server_address = ("127.0.0.1", 54321)
|
|
173
|
+
mock_server.serve_forever.side_effect = KeyboardInterrupt
|
|
174
|
+
server = GHPageServer(
|
|
175
|
+
directory=tmp_path,
|
|
176
|
+
port=0,
|
|
177
|
+
)
|
|
178
|
+
server.start()
|
|
179
|
+
assert server._port == 54321
|
{ghps-0.2 → ghps-0.3}/AUTHORS.md
RENAMED
|
File without changes
|
{ghps-0.2 → ghps-0.3}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ghps-0.2 → ghps-0.3}/setup.cfg
RENAMED
|
File without changes
|