Skip to content

Commit 8f0b232

Browse files
committed
about to start big Core class refactor
1 parent cf17c92 commit 8f0b232

File tree

10 files changed

+499
-178
lines changed

10 files changed

+499
-178
lines changed

pyproject.toml

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ classifiers = [
3232
"Programming Language :: Python :: 3.12",
3333
]
3434

35+
[project.optional-dependencies]
36+
test = [
37+
"pytest>=7.4.0",
38+
"pytest-cov>=4.1.0",
39+
"pytest-mock>=3.12.0",
40+
"pytest-asyncio>=0.23.0",
41+
"pytest-watch >=4.2.0"
42+
]
43+
dev = [
44+
"ruff>=0.3.0",
45+
]
46+
3547
[project.scripts]
3648
socketcli = "socketsecurity.socketcli:cli"
3749

@@ -45,4 +57,97 @@ include = [
4557
]
4658

4759
[tool.setuptools.dynamic]
48-
version = {attr = "socketsecurity.__version__"}
60+
version = {attr = "socketsecurity.__version__"}
61+
62+
[tool.pytest.ini_options]
63+
minversion = "7.0"
64+
addopts = "-ra -q --cov=socketsecurity --cov-report=term-missing"
65+
testpaths = [
66+
"tests",
67+
]
68+
pythonpath = "."
69+
70+
[tool.coverage.run]
71+
source = ["socketsecurity"]
72+
omit = ["tests/*", "**/__init__.py"]
73+
74+
[tool.coverage.report]
75+
exclude_lines = [
76+
"pragma: no cover",
77+
"def __repr__",
78+
"if __name__ == .__main__.:",
79+
"raise NotImplementedError",
80+
"if TYPE_CHECKING:",
81+
]
82+
83+
[tool.ruff]
84+
# Exclude a variety of commonly ignored directories.
85+
exclude = [
86+
".bzr",
87+
".direnv",
88+
".eggs",
89+
".git",
90+
".git-rewrite",
91+
".hg",
92+
".ipynb_checkpoints",
93+
".mypy_cache",
94+
".nox",
95+
".pants.d",
96+
".pyenv",
97+
".pytest_cache",
98+
".pytype",
99+
".ruff_cache",
100+
".svn",
101+
".tox",
102+
".venv",
103+
".vscode",
104+
"__pypackages__",
105+
"_build",
106+
"buck-out",
107+
"build",
108+
"dist",
109+
"node_modules",
110+
"site-packages",
111+
"venv",
112+
]
113+
114+
[tool.ruff.lint]
115+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
116+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
117+
# McCabe complexity (`C901`) by default.
118+
select = ["E4", "E7", "E9", "F"]
119+
ignore = []
120+
121+
# Allow fix for all enabled rules (when `--fix`) is provided.
122+
fixable = ["ALL"]
123+
unfixable = []
124+
125+
# Allow unused variables when underscore-prefixed.
126+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
127+
128+
[tool.ruff.format]
129+
# Like Black, use double quotes for strings.
130+
quote-style = "double"
131+
132+
# Like Black, indent with spaces, rather than tabs.
133+
indent-style = "space"
134+
135+
# Like Black, respect magic trailing commas.
136+
skip-magic-trailing-comma = false
137+
138+
# Like Black, automatically detect the appropriate line ending.
139+
line-ending = "auto"
140+
141+
# Enable auto-formatting of code examples in docstrings. Markdown,
142+
# reStructuredText code/literal blocks and doctests are all supported.
143+
#
144+
# This is currently disabled by default, but it is planned for this
145+
# to be opt-out in the future.
146+
docstring-code-format = false
147+
148+
# Set the line length limit used when formatting code snippets in
149+
# docstrings.
150+
#
151+
# This only has an effect when the `docstring-code-format` setting is
152+
# enabled.
153+
docstring-code-line-length = "dynamic"

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
; addopts = -v --no-cov --capture=no
3+
addopts = -v --no-cov --tb=short -ra

socketsecurity/core/__init__.py

Lines changed: 8 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import logging
22
from pathlib import PurePath
3-
3+
from typing import Optional
4+
from .config import CoreConfig
5+
from .utils import encode_key, do_request, socket_globs
46
import requests
57
from urllib.parse import urlencode
68
import base64
@@ -25,6 +27,7 @@
2527
import platform
2628
from glob import glob
2729
import time
30+
import sys
2831

2932
__all__ = [
3033
"Core",
@@ -49,182 +52,6 @@
4952
log = logging.getLogger("socketdev")
5053
log.addHandler(logging.NullHandler())
5154

52-
socket_globs = {
53-
"spdx": {
54-
"spdx.json": {
55-
"pattern": "*[-.]spdx.json"
56-
}
57-
},
58-
"cdx": {
59-
"cyclonedx.json": {
60-
"pattern": "{bom,*[-.]c{yclone,}dx}.json"
61-
},
62-
"xml": {
63-
"pattern": "{bom,*[-.]c{yclone,}dx}.xml"
64-
}
65-
},
66-
"npm": {
67-
"package.json": {
68-
"pattern": "package.json"
69-
},
70-
"package-lock.json": {
71-
"pattern": "package-lock.json"
72-
},
73-
"npm-shrinkwrap.json": {
74-
"pattern": "npm-shrinkwrap.json"
75-
},
76-
"yarn.lock": {
77-
"pattern": "yarn.lock"
78-
},
79-
"pnpm-lock.yaml": {
80-
"pattern": "pnpm-lock.yaml"
81-
},
82-
"pnpm-lock.yml": {
83-
"pattern": "pnpm-lock.yml"
84-
},
85-
"pnpm-workspace.yaml": {
86-
"pattern": "pnpm-workspace.yaml"
87-
},
88-
"pnpm-workspace.yml": {
89-
"pattern": "pnpm-workspace.yml"
90-
}
91-
},
92-
"pypi": {
93-
"pipfile": {
94-
"pattern": "pipfile"
95-
},
96-
"pyproject.toml": {
97-
"pattern": "pyproject.toml"
98-
},
99-
"poetry.lock": {
100-
"pattern": "poetry.lock"
101-
},
102-
"requirements.txt": {
103-
"pattern": "*requirements.txt"
104-
},
105-
"requirements": {
106-
"pattern": "requirements/*.txt"
107-
},
108-
"requirements-*.txt": {
109-
"pattern": "requirements-*.txt"
110-
},
111-
"requirements_*.txt": {
112-
"pattern": "requirements_*.txt"
113-
},
114-
"requirements.frozen": {
115-
"pattern": "requirements.frozen"
116-
},
117-
"setup.py": {
118-
"pattern": "setup.py"
119-
}
120-
},
121-
"golang": {
122-
"go.mod": {
123-
"pattern": "go.mod"
124-
},
125-
"go.sum": {
126-
"pattern": "go.sum"
127-
}
128-
},
129-
"java": {
130-
"pom.xml": {
131-
"pattern": "pom.xml"
132-
}
133-
}
134-
}
135-
136-
137-
def encode_key(token: str) -> None:
138-
"""
139-
encode_key takes passed token string and does a base64 encoding. It sets this as a global variable
140-
:param token: str of the Socket API Security Token
141-
:return:
142-
"""
143-
global encoded_key
144-
encoded_key = base64.b64encode(token.encode()).decode('ascii')
145-
146-
147-
def do_request(
148-
path: str,
149-
headers: dict = None,
150-
payload: [dict, str] = None,
151-
files: list = None,
152-
method: str = "GET",
153-
base_url: str = None,
154-
) -> requests.request:
155-
"""
156-
do_requests is the shared function for making HTTP calls
157-
:param base_url:
158-
:param path: Required path for the request
159-
:param headers: Optional dictionary of headers. If not set will use a default set
160-
:param payload: Optional dictionary or string of the payload to pass
161-
:param files: Optional list of files to upload
162-
:param method: Optional method to use, defaults to GET
163-
:return:
164-
"""
165-
166-
if base_url is not None:
167-
url = f"{base_url}/{path}"
168-
else:
169-
if encoded_key is None or encoded_key == "":
170-
raise APIKeyMissing
171-
url = f"{api_url}/{path}"
172-
173-
if headers is None:
174-
headers = {
175-
'Authorization': f"Basic {encoded_key}",
176-
'User-Agent': f'SocketPythonCLI/{__version__}',
177-
"accept": "application/json"
178-
}
179-
verify = True
180-
if allow_unverified_ssl:
181-
verify = False
182-
response = requests.request(
183-
method.upper(),
184-
url,
185-
headers=headers,
186-
data=payload,
187-
files=files,
188-
timeout=timeout,
189-
verify=verify
190-
)
191-
output_headers = headers.copy()
192-
output_headers['Authorization'] = "API_KEY_REDACTED"
193-
output = {
194-
"url": url,
195-
"headers": output_headers,
196-
"status_code": response.status_code,
197-
"body": response.text,
198-
"payload": payload,
199-
"files": files,
200-
"timeout": timeout
201-
}
202-
log.debug(output)
203-
if response.status_code <= 399:
204-
return response
205-
elif response.status_code == 400:
206-
raise APIFailure(output)
207-
elif response.status_code == 401:
208-
raise APIAccessDenied("Unauthorized")
209-
elif response.status_code == 403:
210-
raise APIInsufficientQuota("Insufficient max_quota for API method")
211-
elif response.status_code == 404:
212-
raise APIResourceNotFound(f"Path not found {path}")
213-
elif response.status_code == 429:
214-
raise APIInsufficientQuota("Insufficient quota for API route")
215-
elif response.status_code == 524:
216-
raise APICloudflareError(response.text)
217-
else:
218-
msg = {
219-
"status_code": response.status_code,
220-
"UnexpectedError": "There was an unexpected error using the API",
221-
"error": response.text,
222-
"payload": payload,
223-
"url": url
224-
}
225-
raise APIFailure(msg)
226-
227-
22855
class Core:
22956
token: str
23057
base_api_url: str
@@ -291,7 +118,11 @@ def set_timeout(request_timeout: int):
291118
:return:
292119
"""
293120
global timeout
121+
print(f"Setting timeout in module {__name__} at {id(sys.modules[__name__])}")
122+
print(f"Current timeout value: {timeout}")
123+
print(f"Setting to: {request_timeout}")
294124
timeout = request_timeout
125+
print(f"New timeout value: {timeout}")
295126

296127
@staticmethod
297128
def get_org_id_slug() -> (str, str):

socketsecurity/core/config.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from dataclasses import dataclass
2+
from typing import ClassVar
3+
4+
@dataclass
5+
class CoreConfig:
6+
"""Configuration for the Socket Security Core class"""
7+
8+
# Required
9+
token: str
10+
11+
# Optional with defaults
12+
api_url: str = "https://api.socket.dev/v0"
13+
timeout: int = 30
14+
enable_all_alerts: bool = False
15+
allow_unverified_ssl: bool = False
16+
17+
# Constants
18+
SOCKET_DATE_FORMAT: ClassVar[str] = "%Y-%m-%dT%H:%M:%S.%fZ"
19+
DEFAULT_API_URL: ClassVar[str] = "https://api.socket.dev/v0"
20+
DEFAULT_TIMEOUT: ClassVar[int] = 30
21+
22+
def __post_init__(self) -> None:
23+
"""Validate and process config after initialization"""
24+
# Business rule validations
25+
if not self.token:
26+
raise ValueError("Token is required")
27+
if self.timeout <= 0:
28+
raise ValueError("Timeout must be positive")
29+
30+
# Business logic
31+
if not self.token.endswith(':'):
32+
self.token = f"{self.token}:"

0 commit comments

Comments
 (0)