wirez-py 1.0.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.
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: wirez-py
3
+ Version: 1.0.0
4
+ Summary: Remember and retrieve file paths by name — from any program, anywhere.
5
+ Author:
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kulbir/wires
8
+ Keywords: files,path,registry,bookmark,wires
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Dynamic: requires-python
15
+
16
+ # wires
17
+
18
+ **Remember and retrieve file paths by name — from any program, anywhere.**
19
+
20
+ ```
21
+ pip install wires
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Usage
27
+
28
+ ```python
29
+ import wires
30
+
31
+ # Save / remember a file
32
+ wires.save("my_config", "/path/to/config.json")
33
+ wires.remember("my_config", "/path/to/config.json") # same thing
34
+
35
+ # Retrieve it later — from any program
36
+ wires.get("my_config") # → "/path/to/config.json" (str)
37
+ wires.recall("my_config") # same thing
38
+ wires.path("my_config") # → pathlib.Path
39
+
40
+ # See all saved files
41
+ wires.list_all() # → {"my_config": "/path/to/config.json", ...}
42
+
43
+ # Remove
44
+ wires.remove("my_config")
45
+ ```
46
+
47
+ ## How it works
48
+
49
+ Wires stores all registrations in `~/.wires/registry.json`. Every program on the machine shares this registry, so a path saved in one script is instantly accessible in another.
50
+
51
+ ## License
52
+
53
+ MIT
@@ -0,0 +1,38 @@
1
+ # wires
2
+
3
+ **Remember and retrieve file paths by name — from any program, anywhere.**
4
+
5
+ ```
6
+ pip install wires
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ import wires
15
+
16
+ # Save / remember a file
17
+ wires.save("my_config", "/path/to/config.json")
18
+ wires.remember("my_config", "/path/to/config.json") # same thing
19
+
20
+ # Retrieve it later — from any program
21
+ wires.get("my_config") # → "/path/to/config.json" (str)
22
+ wires.recall("my_config") # same thing
23
+ wires.path("my_config") # → pathlib.Path
24
+
25
+ # See all saved files
26
+ wires.list_all() # → {"my_config": "/path/to/config.json", ...}
27
+
28
+ # Remove
29
+ wires.remove("my_config")
30
+ ```
31
+
32
+ ## How it works
33
+
34
+ Wires stores all registrations in `~/.wires/registry.json`. Every program on the machine shares this registry, so a path saved in one script is instantly accessible in another.
35
+
36
+ ## License
37
+
38
+ MIT
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "wirez-py"
7
+ version = "1.0.0"
8
+ description = "Remember and retrieve file paths by name — from any program, anywhere."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ keywords = ["files", "path", "registry", "bookmark", "wires"]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://github.com/kulbir/wires"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="wirez-py",
5
+ version="1.0.0",
6
+ description="Remember and retrieve file paths by name, across any program.",
7
+ author="",
8
+ python_requires=">=3.10",
9
+ packages=find_packages(),
10
+ )
@@ -0,0 +1,183 @@
1
+ """
2
+ wires test suite
3
+ Run with: pytest tests/ -v
4
+ """
5
+
6
+ import pytest
7
+ import tempfile
8
+ import os
9
+ from pathlib import Path
10
+
11
+ import wires
12
+ from wires import registry, debug as dbg
13
+
14
+
15
+ # ── Fixtures ──────────────────────────────────────────────────────────────────
16
+
17
+ @pytest.fixture(autouse=True)
18
+ def isolated_registry(tmp_path, monkeypatch):
19
+ """Redirect the registry to a temp dir so tests never touch ~/.wires"""
20
+ reg_dir = tmp_path / ".wires"
21
+ reg_file = reg_dir / "registry.json"
22
+ monkeypatch.setattr(registry, "_REGISTRY_DIR", reg_dir)
23
+ monkeypatch.setattr(registry, "_REGISTRY_FILE", reg_file)
24
+ yield reg_file
25
+
26
+
27
+ @pytest.fixture
28
+ def real_file(tmp_path):
29
+ """A real file on disk to use in tests."""
30
+ f = tmp_path / "real.txt"
31
+ f.write_text("hello")
32
+ return str(f)
33
+
34
+
35
+ @pytest.fixture
36
+ def fake_path():
37
+ return "/nonexistent/path/file.txt"
38
+
39
+
40
+ # ── save / remember ───────────────────────────────────────────────────────────
41
+
42
+ class TestSave:
43
+ def test_save_stores_path(self, real_file):
44
+ wires.save("myfile", real_file)
45
+ assert wires.get("myfile") == str(Path(real_file).resolve())
46
+
47
+ def test_remember_is_alias_for_save(self, real_file):
48
+ wires.remember("alias", real_file)
49
+ assert wires.get("alias") == str(Path(real_file).resolve())
50
+
51
+ def test_save_overwrites_existing(self, real_file, tmp_path):
52
+ second = tmp_path / "second.txt"
53
+ second.write_text("world")
54
+ wires.save("key", real_file)
55
+ wires.save("key", str(second))
56
+ assert wires.get("key") == str(second.resolve())
57
+
58
+ def test_save_resolves_relative_path(self, tmp_path, monkeypatch):
59
+ monkeypatch.chdir(tmp_path)
60
+ f = tmp_path / "rel.txt"
61
+ f.write_text("x")
62
+ wires.save("rel", "rel.txt")
63
+ result = wires.get("rel")
64
+ assert Path(result).is_absolute()
65
+
66
+ def test_save_nonexistent_file_still_works(self, fake_path):
67
+ # should not raise — just warn
68
+ wires.save("ghost", fake_path)
69
+ assert "ghost" in wires.list_all()
70
+
71
+
72
+ # ── get / recall ──────────────────────────────────────────────────────────────
73
+
74
+ class TestGet:
75
+ def test_get_returns_string(self, real_file):
76
+ wires.save("f", real_file)
77
+ result = wires.get("f")
78
+ assert isinstance(result, str)
79
+
80
+ def test_recall_is_alias_for_get(self, real_file):
81
+ wires.save("f", real_file)
82
+ assert wires.recall("f") == wires.get("f")
83
+
84
+ def test_get_missing_raises_key_error(self):
85
+ with pytest.raises(KeyError, match="nothere"):
86
+ wires.get("nothere")
87
+
88
+ def test_path_returns_path_object(self, real_file):
89
+ wires.save("f", real_file)
90
+ result = wires.path("f")
91
+ assert isinstance(result, Path)
92
+
93
+ def test_path_missing_raises_key_error(self):
94
+ with pytest.raises(KeyError):
95
+ wires.path("missing")
96
+
97
+
98
+ # ── remove ────────────────────────────────────────────────────────────────────
99
+
100
+ class TestRemove:
101
+ def test_remove_deletes_entry(self, real_file):
102
+ wires.save("x", real_file)
103
+ wires.remove("x")
104
+ assert "x" not in wires.list_all()
105
+
106
+ def test_remove_missing_raises_key_error(self):
107
+ with pytest.raises(KeyError):
108
+ wires.remove("doesnotexist")
109
+
110
+ def test_remove_then_get_raises(self, real_file):
111
+ wires.save("x", real_file)
112
+ wires.remove("x")
113
+ with pytest.raises(KeyError):
114
+ wires.get("x")
115
+
116
+
117
+ # ── list_all ──────────────────────────────────────────────────────────────────
118
+
119
+ class TestListAll:
120
+ def test_empty_registry(self):
121
+ assert wires.list_all() == {}
122
+
123
+ def test_lists_multiple_entries(self, real_file, tmp_path):
124
+ b = tmp_path / "b.txt"
125
+ b.write_text("b")
126
+ wires.save("a", real_file)
127
+ wires.save("b", str(b))
128
+ result = wires.list_all()
129
+ assert "a" in result
130
+ assert "b" in result
131
+
132
+ def test_list_all_is_copy(self, real_file):
133
+ wires.save("k", real_file)
134
+ result = wires.list_all()
135
+ result["injected"] = "evil"
136
+ assert "injected" not in wires.list_all()
137
+
138
+
139
+ # ── debug system ──────────────────────────────────────────────────────────────
140
+
141
+ class TestDebug:
142
+ def test_debug_log_outputs_to_stderr(self, capsys, real_file):
143
+ wires.enable_debug(True)
144
+ wires.save("d", real_file)
145
+ captured = capsys.readouterr()
146
+ assert "[wires debug]" in captured.err or "[wires ok]" in captured.err
147
+ wires.enable_debug(False)
148
+
149
+ def test_debug_off_produces_no_output(self, capsys, real_file):
150
+ wires.enable_debug(False)
151
+ wires.save("silent", real_file)
152
+ wires.get("silent")
153
+ captured = capsys.readouterr()
154
+ assert "[wires debug]" not in captured.err
155
+
156
+ def test_warn_on_overwrite(self, capsys, real_file):
157
+ wires.enable_warnings(True)
158
+ wires.save("w", real_file)
159
+ wires.save("w", real_file)
160
+ captured = capsys.readouterr()
161
+ assert "[wires warn]" in captured.err
162
+
163
+ def test_warn_on_missing_file(self, capsys, fake_path):
164
+ wires.enable_warnings(True)
165
+ wires.save("ghost", fake_path)
166
+ captured = capsys.readouterr()
167
+ assert "[wires warn]" in captured.err
168
+
169
+ def test_warn_stale_path_in_list_all(self, capsys, real_file, tmp_path):
170
+ wires.enable_warnings(True)
171
+ wires.save("stale", real_file)
172
+ # delete the file after saving
173
+ Path(real_file).unlink()
174
+ wires.list_all()
175
+ captured = capsys.readouterr()
176
+ assert "[wires warn]" in captured.err
177
+
178
+ def test_disable_warnings(self, capsys, fake_path):
179
+ wires.enable_warnings(False)
180
+ wires.save("q", fake_path)
181
+ captured = capsys.readouterr()
182
+ assert "[wires warn]" not in captured.err
183
+ wires.enable_warnings(True)
@@ -0,0 +1,37 @@
1
+ """
2
+ wires — remember and retrieve file paths by name.
3
+ """
4
+
5
+ from .registry import save, remember, get, recall, remove, list_all, path
6
+ from .debug import enable_debug, enable_warnings
7
+
8
+ __all__ = [
9
+ "save", "remember",
10
+ "get", "recall",
11
+ "remove", "list_all", "path",
12
+ "enable_debug", "enable_warnings",
13
+ ]
14
+
15
+ print(r"""
16
+ _____ _____ _____ _____ _____
17
+ /\ \ /\ \ /\ \ /\ \ /\ \
18
+ /::\____\ /::\ \ /::\ \ /::\ \ /::\ \
19
+ /:::/ / \:::\ \ /::::\ \ /::::\ \ /::::\ \
20
+ /:::/ _/___ \:::\ \ /::::::\ \ /::::::\ \ /::::::\ \
21
+ /:::/ /\ \ \:::\ \ /:::/\:::\ \ /:::/\:::\ \ /:::/\:::\ \
22
+ /:::/ /::\____\ \:::\ \ /:::/__\:::\ \ /:::/__\:::\ \ /:::/__\:::\ \
23
+ /:::/ /:::/ / /::::\ \ /::::\ \:::\ \ /::::\ \:::\ \ \:::\ \:::\ \
24
+ /:::/ /:::/ _/___ ____ /::::::\ \ /::::::\ \:::\ \ /::::::\ \:::\ \ ___\:::\ \:::\ \
25
+ /:::/___/:::/ /\ \ /\ \ /:::/\:::\ \ /:::/\:::\ \:::\____\ /:::/\:::\ \:::\ \ /\ \:::\ \:::\ \
26
+ |:::| /:::/ /::\____\/::\ \/:::/ \:::\____\/:::/ \:::\ \:::| |/:::/__\:::\ \:::\____\/::\ \:::\ \:::\____\
27
+ |:::|__/:::/ /:::/ /\:::\ /:::/ \::/ /\::/ |::::\ /:::|____|\:::\ \:::\ \::/ /\:::\ \:::\ \::/ /
28
+ \:::\/:::/ /:::/ / \:::\/:::/ / \/____/ \/____|:::::\/:::/ / \:::\ \:::\ \/____/ \:::\ \:::\ \/____/
29
+ \::::::/ /:::/ / \::::::/ / |:::::::::/ / \:::\ \:::\ \ \:::\ \:::\ \
30
+ \::::/___/:::/ / \::::/____/ |::|\::::/ / \:::\ \:::\____\ \:::\ \:::\____\
31
+ \:::\__/:::/ / \:::\ \ |::| \::/____/ \:::\ \::/ / \:::\ /:::/ /
32
+ \::::::::/ / \:::\ \ |::| ~| \:::\ \/____/ \:::\/:::/ /
33
+ \::::::/ / \:::\ \ |::| | \:::\ \ \::::::/ /
34
+ \::::/ / \:::\____\ \::| | \:::\____\ \::::/ /
35
+ \::/____/ \::/ / \:| | \::/ / \::/ /
36
+ ~~ \/____/ \|___| \/____/ \/____/
37
+ """)
@@ -0,0 +1,47 @@
1
+ import os
2
+ import sys
3
+
4
+ _debug_enabled = False
5
+ _warn_enabled = True
6
+
7
+ RESET = "\033[0m"
8
+ YELLOW = "\033[93m"
9
+ RED = "\033[91m"
10
+ CYAN = "\033[96m"
11
+ GREEN = "\033[92m"
12
+ BOLD = "\033[1m"
13
+
14
+ _SUPPORTS_COLOR = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
15
+
16
+
17
+ def _color(code: str, text: str) -> str:
18
+ return f"{code}{text}{RESET}" if _SUPPORTS_COLOR else text
19
+
20
+
21
+ def enable_debug(on: bool = True) -> None:
22
+ global _debug_enabled
23
+ _debug_enabled = on
24
+
25
+
26
+ def enable_warnings(on: bool = True) -> None:
27
+ global _warn_enabled
28
+ _warn_enabled = on
29
+
30
+
31
+ def log(msg: str) -> None:
32
+ if _debug_enabled:
33
+ print(_color(CYAN, f"[wires debug] {msg}"), file=sys.stderr)
34
+
35
+
36
+ def warn(msg: str) -> None:
37
+ if _warn_enabled:
38
+ print(_color(YELLOW, f"[wires warn] {msg}"), file=sys.stderr)
39
+
40
+
41
+ def error(msg: str) -> None:
42
+ print(_color(RED, f"[wires error] {msg}"), file=sys.stderr)
43
+
44
+
45
+ def ok(msg: str) -> None:
46
+ if _debug_enabled:
47
+ print(_color(GREEN, f"[wires ok] {msg}"), file=sys.stderr)
@@ -0,0 +1,94 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from . import debug as _dbg
5
+
6
+ _REGISTRY_DIR = Path.home() / ".wires"
7
+ _REGISTRY_FILE = _REGISTRY_DIR / "registry.json"
8
+
9
+
10
+ def _load() -> dict:
11
+ if not _REGISTRY_FILE.exists():
12
+ _dbg.log(f"Registry not found at {_REGISTRY_FILE}, starting empty")
13
+ return {}
14
+ with open(_REGISTRY_FILE, "r", encoding="utf-8") as f:
15
+ data = json.load(f)
16
+ _dbg.log(f"Loaded registry ({len(data)} entries)")
17
+ return data
18
+
19
+
20
+ def _dump(data: dict) -> None:
21
+ _REGISTRY_DIR.mkdir(parents=True, exist_ok=True)
22
+ with open(_REGISTRY_FILE, "w", encoding="utf-8") as f:
23
+ json.dump(data, f, indent=2)
24
+ _dbg.log(f"Registry saved to {_REGISTRY_FILE}")
25
+
26
+
27
+ def save(name: str, file_path: str | os.PathLike) -> None:
28
+ """Register a file path under a name."""
29
+ resolved = str(Path(file_path).resolve())
30
+
31
+ data = _load()
32
+
33
+ if name in data:
34
+ _dbg.warn(f"'{name}' already exists ({data[name]}) — overwriting with {resolved}")
35
+ else:
36
+ _dbg.log(f"Registering new wire: '{name}' → {resolved}")
37
+
38
+ if not Path(resolved).exists():
39
+ _dbg.warn(f"The file '{resolved}' does not exist on disk — saving path anyway")
40
+
41
+ data[name] = resolved
42
+ _dump(data)
43
+ _dbg.ok(f"Saved '{name}'")
44
+
45
+
46
+ # alias
47
+ remember = save
48
+
49
+
50
+ def get(name: str) -> str:
51
+ """Return the path string registered under name."""
52
+ data = _load()
53
+ if name not in data:
54
+ _dbg.error(f"No wire named '{name}' — available: {list(data.keys())}")
55
+ raise KeyError(f"No wire named '{name}'")
56
+
57
+ result = data[name]
58
+ _dbg.log(f"Retrieved '{name}' → {result}")
59
+
60
+ if not Path(result).exists():
61
+ _dbg.warn(f"Wire '{name}' points to '{result}' which no longer exists on disk")
62
+
63
+ return result
64
+
65
+
66
+ # alias
67
+ recall = get
68
+
69
+
70
+ def path(name: str) -> Path:
71
+ """Return the registered path as a pathlib.Path."""
72
+ return Path(get(name))
73
+
74
+
75
+ def remove(name: str) -> None:
76
+ """Unregister a name."""
77
+ data = _load()
78
+ if name not in data:
79
+ _dbg.error(f"Cannot remove '{name}' — not found. Available: {list(data.keys())}")
80
+ raise KeyError(f"No wire named '{name}'")
81
+ old = data.pop(name)
82
+ _dump(data)
83
+ _dbg.ok(f"Removed '{name}' (was → {old})")
84
+
85
+
86
+ def list_all() -> dict[str, str]:
87
+ """Return a copy of all registered name→path mappings."""
88
+ data = _load()
89
+ _dbg.log(f"Listing all wires: {list(data.keys())}")
90
+ # warn about any stale paths
91
+ for name, p in data.items():
92
+ if not Path(p).exists():
93
+ _dbg.warn(f"Stale wire: '{name}' → '{p}' (file not found)")
94
+ return dict(data)
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: wirez-py
3
+ Version: 1.0.0
4
+ Summary: Remember and retrieve file paths by name — from any program, anywhere.
5
+ Author:
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kulbir/wires
8
+ Keywords: files,path,registry,bookmark,wires
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Dynamic: requires-python
15
+
16
+ # wires
17
+
18
+ **Remember and retrieve file paths by name — from any program, anywhere.**
19
+
20
+ ```
21
+ pip install wires
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Usage
27
+
28
+ ```python
29
+ import wires
30
+
31
+ # Save / remember a file
32
+ wires.save("my_config", "/path/to/config.json")
33
+ wires.remember("my_config", "/path/to/config.json") # same thing
34
+
35
+ # Retrieve it later — from any program
36
+ wires.get("my_config") # → "/path/to/config.json" (str)
37
+ wires.recall("my_config") # same thing
38
+ wires.path("my_config") # → pathlib.Path
39
+
40
+ # See all saved files
41
+ wires.list_all() # → {"my_config": "/path/to/config.json", ...}
42
+
43
+ # Remove
44
+ wires.remove("my_config")
45
+ ```
46
+
47
+ ## How it works
48
+
49
+ Wires stores all registrations in `~/.wires/registry.json`. Every program on the machine shares this registry, so a path saved in one script is instantly accessible in another.
50
+
51
+ ## License
52
+
53
+ MIT
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ tests/test_wires.py
5
+ wires/__init__.py
6
+ wires/debug.py
7
+ wires/registry.py
8
+ wirez_py.egg-info/PKG-INFO
9
+ wirez_py.egg-info/SOURCES.txt
10
+ wirez_py.egg-info/dependency_links.txt
11
+ wirez_py.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ wires