fredq 0.1.0__py3-none-any.whl

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.
fredq/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Expose FRED (Federal Reserve Economic Data) endpoints as a raw JSON CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__ = "0.1.0"
6
+
7
+ __all__ = ["__version__"]
fredq/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Run fredq as a module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fredq.cli import main
6
+
7
+ if __name__ == "__main__":
8
+ raise SystemExit(main())
fredq/auth.py ADDED
@@ -0,0 +1,114 @@
1
+ """FRED API key resolution."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Final, TextIO
9
+
10
+ from fredq.exceptions import FredApiKeyMissingError
11
+
12
+ _ENV_VAR: Final[str] = "FRED_API_KEY"
13
+ # On POSIX, warn when the key file is readable by group or world.
14
+ _WIDE_MODE_MASK: Final[int] = 0o077
15
+
16
+
17
+ def default_key_path() -> Path:
18
+ """Return fredq's default API key file path.
19
+
20
+ Returns:
21
+ Path: Default location for the FRED API key file.
22
+ """
23
+
24
+ return Path.home() / ".fredq" / "api_key"
25
+
26
+
27
+ def _check_key_file_permissions(path: Path, stderr: TextIO) -> None:
28
+ """Emit a one-line warning to ``stderr`` when ``path`` is world- or group-readable.
29
+
30
+ Only runs on POSIX (``os.name != 'nt'``). Windows ACL checks are
31
+ too platform-specific and are skipped.
32
+
33
+ Args:
34
+ path: Path to the API key file.
35
+ stderr: Stream for the permission warning.
36
+ """
37
+
38
+ if os.name == "nt":
39
+ return
40
+ try:
41
+ mode = path.stat().st_mode
42
+ except OSError:
43
+ return
44
+ if mode & _WIDE_MODE_MASK:
45
+ stderr.write(
46
+ f"warning: {path} is readable by group or world; "
47
+ "run `chmod 600` to restrict access.\n"
48
+ )
49
+
50
+
51
+ def _read_key_file(path: Path, stderr: TextIO) -> str | None:
52
+ if not path.exists():
53
+ return None
54
+ _check_key_file_permissions(path, stderr)
55
+ try:
56
+ contents = path.read_text(encoding="utf-8").strip()
57
+ except OSError:
58
+ return None
59
+ if not contents:
60
+ return None
61
+ # Take only the first non-empty line so trailing whitespace or a
62
+ # comment-style second line cannot leak into the request.
63
+ return contents.splitlines()[0].strip() or None
64
+
65
+
66
+ def resolve_api_key(
67
+ *,
68
+ explicit: str | None = None,
69
+ key_path: Path | None = None,
70
+ use_key_file: bool = True,
71
+ stderr: TextIO | None = None,
72
+ ) -> str:
73
+ """Resolve the FRED API key.
74
+
75
+ Lookup order:
76
+ 1. ``explicit`` argument (e.g. from a CLI override).
77
+ 2. ``FRED_API_KEY`` environment variable.
78
+ 3. Single-line file at ``key_path`` (defaults to ``~/.fredq/api_key``),
79
+ unless ``use_key_file=False``.
80
+
81
+ Args:
82
+ explicit: Explicit API key from a CLI override; ignored when ``None``.
83
+ key_path: Override the fallback file path. Defaults to
84
+ :func:`default_key_path`.
85
+ use_key_file: When ``False``, skip the file fallback entirely. Set
86
+ by ``--no-key-file`` or ``FREDQ_DISABLE_KEY_FILE=1``.
87
+ stderr: Stream for the permission warning. Defaults to
88
+ ``sys.stderr`` when ``None``.
89
+
90
+ Returns:
91
+ str: A non-empty FRED API key.
92
+
93
+ Raises:
94
+ FredApiKeyMissingError: If no key can be located by any mechanism.
95
+ """
96
+
97
+ err = stderr if stderr is not None else sys.stderr
98
+
99
+ if explicit:
100
+ stripped = explicit.strip()
101
+ if stripped:
102
+ return stripped
103
+
104
+ env_value = os.environ.get(_ENV_VAR, "").strip()
105
+ if env_value:
106
+ return env_value
107
+
108
+ if use_key_file:
109
+ path = key_path or default_key_path()
110
+ from_file = _read_key_file(path, err)
111
+ if from_file:
112
+ return from_file
113
+
114
+ raise FredApiKeyMissingError