-
Notifications
You must be signed in to change notification settings - Fork 114
Expand file tree
/
Copy pathregistry.py
More file actions
130 lines (94 loc) · 3.73 KB
/
registry.py
File metadata and controls
130 lines (94 loc) · 3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""Manage registered marketplaces in ``~/.apm/marketplaces.json``."""
import json
import logging
import os
from typing import Dict, List, Optional
from .errors import MarketplaceNotFoundError
from .models import MarketplaceSource
logger = logging.getLogger(__name__)
_MARKETPLACES_FILENAME = "marketplaces.json"
# Process-lifetime cache --------------------------------------------------
_registry_cache: Optional[List[MarketplaceSource]] = None
def _marketplaces_path() -> str:
"""Return the full path to ``~/.apm/marketplaces.json``."""
from ..config import CONFIG_DIR
return os.path.join(CONFIG_DIR, _MARKETPLACES_FILENAME)
def _ensure_file() -> str:
"""Ensure the marketplaces file exists, creating it if needed."""
from ..config import ensure_config_exists
ensure_config_exists()
path = _marketplaces_path()
if not os.path.exists(path):
with open(path, "w", encoding="utf-8") as f:
json.dump({"marketplaces": []}, f, indent=2)
return path
def _invalidate_cache() -> None:
global _registry_cache
_registry_cache = None
def _load() -> List[MarketplaceSource]:
"""Load registered marketplaces from disk (cached per-process)."""
global _registry_cache
if _registry_cache is not None:
return list(_registry_cache)
path = _ensure_file()
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
except (json.JSONDecodeError, OSError) as exc:
logger.warning("Failed to read %s: %s", path, exc)
data = {"marketplaces": []}
sources: List[MarketplaceSource] = []
for entry in data.get("marketplaces", []):
try:
sources.append(MarketplaceSource.from_dict(entry))
except (KeyError, TypeError) as exc:
logger.debug("Skipping invalid marketplace entry: %s", exc)
_registry_cache = sources
return list(sources)
def _save(sources: List[MarketplaceSource]) -> None:
"""Write marketplace list to disk atomically."""
global _registry_cache
path = _ensure_file()
data = {"marketplaces": [s.to_dict() for s in sources]}
tmp = path + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
os.replace(tmp, path)
_registry_cache = list(sources)
# Public API ---------------------------------------------------------------
def get_registered_marketplaces() -> List[MarketplaceSource]:
"""Return all registered marketplaces."""
return _load()
def get_marketplace_by_name(name: str) -> MarketplaceSource:
"""Return a marketplace by display name (case-insensitive).
Raises:
MarketplaceNotFoundError: If not found.
"""
lower = name.lower()
for src in _load():
if src.name.lower() == lower:
return src
raise MarketplaceNotFoundError(name)
def add_marketplace(source: MarketplaceSource) -> None:
"""Register a marketplace (replaces if same name exists)."""
sources = [s for s in _load() if s.name.lower() != source.name.lower()]
sources.append(source)
_save(sources)
logger.debug("Registered marketplace '%s'", source.name)
def remove_marketplace(name: str) -> None:
"""Remove a marketplace by name.
Raises:
MarketplaceNotFoundError: If not found.
"""
before = _load()
after = [s for s in before if s.name.lower() != name.lower()]
if len(after) == len(before):
raise MarketplaceNotFoundError(name)
_save(after)
logger.debug("Removed marketplace '%s'", name)
def marketplace_names() -> List[str]:
"""Return sorted list of registered marketplace names."""
return sorted(s.name for s in _load())
def marketplace_count() -> int:
"""Return the number of registered marketplaces."""
return len(_load())