hunterMakesPy 0.1.0__py3-none-any.whl → 0.1.2__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.
hunterMakesPy/__init__.py CHANGED
@@ -1,14 +1,22 @@
1
- """`import hunterMakesPy as humpy`."""
1
+ """A modular toolkit for defensive programming, parameter validation, file system utilities, and data structure manipulation.
2
+
3
+ This package provides:
4
+ - Defensive programming helpers for handling `None` values and error propagation.
5
+ - Parameter and input validation, integer parsing, and concurrency limit utilities.
6
+ - File system and import utilities for safe directory creation and dynamic module/attribute loading.
7
+ - Utilities for string extraction from nested data structures and merging dictionaries of lists.
8
+
9
+ """
2
10
  from hunterMakesPy.theTypes import identifierDotAttribute as identifierDotAttribute
3
11
 
4
- from hunterMakesPy.coping import raiseIfNone as raiseIfNone
12
+ from hunterMakesPy.coping import PackageSettings as PackageSettings, raiseIfNone as raiseIfNone
5
13
 
6
14
  from hunterMakesPy.parseParameters import (defineConcurrencyLimit as defineConcurrencyLimit, intInnit as intInnit,
7
15
  oopsieKwargsie as oopsieKwargsie)
8
16
 
9
17
  from hunterMakesPy.filesystemToolkit import (importLogicalPath2Identifier as importLogicalPath2Identifier,
10
18
  importPathFilename2Identifier as importPathFilename2Identifier, makeDirsSafely as makeDirsSafely,
11
- writeStringToHere as writeStringToHere)
19
+ writeStringToHere as writeStringToHere)
12
20
 
13
21
  from hunterMakesPy.dataStructures import (autoDecodingRLE as autoDecodingRLE, stringItUp as stringItUp,
14
22
  updateExtendPolishDictionaryLists as updateExtendPolishDictionaryLists)
hunterMakesPy/_theSSOT.py CHANGED
@@ -1,40 +1,4 @@
1
- """Primary: settings for this package.
1
+ """Settings for this package."""
2
+ from hunterMakesPy import PackageSettings
2
3
 
3
- Secondary: settings for manufacturing.
4
- Tertiary: hardcoded values until I implement a dynamic solution.
5
- """
6
- from importlib.util import find_spec
7
- from pathlib import Path
8
- from tomli import loads as tomli_loads
9
- from typing import TYPE_CHECKING
10
- import dataclasses
11
-
12
- if TYPE_CHECKING:
13
- from importlib.machinery import ModuleSpec
14
-
15
- try:
16
- identifierPackagePACKAGING: str = tomli_loads(Path("pyproject.toml").read_text())["project"]["name"]
17
- except Exception: # noqa: BLE001
18
- identifierPackagePACKAGING = "hunterMakesPy"
19
-
20
- def getPathPackageINSTALLING() -> Path:
21
- """Return the root directory of the installed package."""
22
- try:
23
- moduleSpecification: ModuleSpec | None = find_spec(identifierPackagePACKAGING)
24
- if moduleSpecification and moduleSpecification.origin:
25
- pathFilename = Path(moduleSpecification.origin)
26
- return pathFilename.parent if pathFilename.is_file() else pathFilename
27
- except ModuleNotFoundError:
28
- pass
29
- return Path.cwd()
30
-
31
- @dataclasses.dataclass
32
- class PackageSettings:
33
- fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
34
- """Default file extension for generated code files."""
35
- identifierPackage: str = dataclasses.field(default = identifierPackagePACKAGING, metadata={'evaluateWhen': 'packaging'})
36
- """Name of this package, used for import paths and configuration."""
37
- pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, metadata={'evaluateWhen': 'installing'})
38
- """Absolute path to the installed package directory."""
39
-
40
- settingsPackage = PackageSettings()
4
+ settingsPackage = PackageSettings(identifierPackageFALLBACK="hunterMakesPy")
hunterMakesPy/coping.py CHANGED
@@ -1,13 +1,89 @@
1
- """Utility functions for handling `None` values and coping with common programming patterns.
1
+ """Package configuration and defensive programming utilities for Python projects."""
2
+ from importlib.util import find_spec
3
+ from pathlib import Path
4
+ from tomllib import loads as tomllib_loads
5
+ from typing import TYPE_CHECKING, TypeVar
6
+ import dataclasses
2
7
 
3
- (AI generated docstring)
8
+ if TYPE_CHECKING:
9
+ from importlib.machinery import ModuleSpec
4
10
 
5
- This module provides helper functions for defensive programming and error handling, particularly for dealing with `None` values that should not occur in correct program flow.
11
+ TypeSansNone = TypeVar('TypeSansNone')
6
12
 
7
- """
8
- from typing import TypeVar
13
+ def getIdentifierPackagePACKAGING(identifierPackageFALLBACK: str) -> str:
14
+ """Get package name from pyproject.toml or fallback to provided value."""
15
+ try:
16
+ return tomllib_loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project']['name']
17
+ except Exception: # noqa: BLE001
18
+ return identifierPackageFALLBACK
19
+
20
+ def getPathPackageINSTALLING(identifierPackage: str) -> Path:
21
+ """Return the root directory of the installed package."""
22
+ try:
23
+ moduleSpecification: ModuleSpec | None = find_spec(identifierPackage)
24
+ if moduleSpecification and moduleSpecification.origin:
25
+ pathFilename = Path(moduleSpecification.origin)
26
+ return pathFilename.parent if pathFilename.is_file() else pathFilename
27
+ except ModuleNotFoundError:
28
+ pass
29
+ return Path.cwd()
30
+
31
+ @dataclasses.dataclass
32
+ class PackageSettings:
33
+ """Configuration container for Python package metadata and runtime settings.
34
+
35
+ This `class` provides a simple way to store and access basic information about a Python package, It will automatically resolve
36
+ package identifiers and installation paths if they are not passed to the `class` constructor. Python `dataclasses` are easy to
37
+ subtype and extend.
9
38
 
10
- TypeSansNone = TypeVar('TypeSansNone')
39
+ Parameters
40
+ ----------
41
+ identifierPackageFALLBACK : str = ''
42
+ Fallback package identifier used only during initialization when automatic discovery fails.
43
+ pathPackage : Path = Path()
44
+ Absolute path to the installed package directory. Automatically resolved from `identifierPackage` if not provided.
45
+ identifierPackage : str = ''
46
+ Canonical name of the package. Automatically extracted from `pyproject.toml`.
47
+ fileExtension : str = '.py'
48
+ Default file extension.
49
+
50
+ Examples
51
+ --------
52
+ Automatic package discovery from development environment:
53
+
54
+ ```python
55
+ settings = PackageSettings(identifierPackageFALLBACK='cobraPy')
56
+ # Automatically discovers package name from pyproject.toml
57
+ # Resolves installation path from package identifier
58
+ ```
59
+
60
+ Explicit configuration for specific deployment:
61
+
62
+ ```python
63
+ settings = PackageSettings(
64
+ identifierPackage='cobraPy',
65
+ pathPackage=Path('/opt/tenEx/packages/cobraPy'),
66
+ fileExtension='.pyx'
67
+ )
68
+ ```
69
+
70
+ """
71
+
72
+ identifierPackageFALLBACK: dataclasses.InitVar[str] = ''
73
+ """Fallback package identifier used during initialization only."""
74
+ pathPackage: Path = dataclasses.field(default_factory=Path, metadata={'evaluateWhen': 'installing'})
75
+ """Absolute path to the installed package."""
76
+ identifierPackage: str = dataclasses.field(default='', metadata={'evaluateWhen': 'packaging'})
77
+ """Name of this package."""
78
+ fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
79
+ """Default file extension for files."""
80
+
81
+ def __post_init__(self, identifierPackageFALLBACK: str) -> None:
82
+ """Initialize computed fields after dataclass initialization."""
83
+ if not self.identifierPackage and identifierPackageFALLBACK:
84
+ self.identifierPackage = getIdentifierPackagePACKAGING(identifierPackageFALLBACK)
85
+ if self.pathPackage == Path() and self.identifierPackage:
86
+ self.pathPackage = getPathPackageINSTALLING(self.identifierPackage)
11
87
 
12
88
  def raiseIfNone(returnTarget: TypeSansNone | None, errorMessage: str | None = None) -> TypeSansNone:
13
89
  """Raise a `ValueError` if the target value is `None`, otherwise return the value: tell the type checker that the return value is not `None`.
@@ -46,7 +46,7 @@ def autoDecodingRLE(arrayTarget: NDArray[integer[Any]], *, assumeAddSpaces: bool
46
46
  """
47
47
  def sliceNDArrayToNestedLists(arraySlice: NDArray[integer[Any]]) -> Any:
48
48
  def getLengthOption(optionAsStr: str) -> int:
49
- # `assumeAddSpaces` characters: `,` 1; `]*` 2 # noqa: ERA001
49
+ """`assumeAddSpaces` characters: `,` 1; `]*` 2."""
50
50
  return assumeAddSpaces * (optionAsStr.count(',') + optionAsStr.count(']*') * 2) + len(optionAsStr)
51
51
 
52
52
  if arraySlice.ndim > 1:
@@ -125,29 +125,29 @@ def autoDecodingRLE(arrayTarget: NDArray[integer[Any]], *, assumeAddSpaces: bool
125
125
  patternRegex = regex.compile(
126
126
  "(?<!rang)(?:"
127
127
  # Pattern 1: Comma ahead, bracket behind # noqa: ERA001
128
- "(?P<joinAhead>,)\\((?P<malkovich>\\d+),(?P<multiple>\\d+)\\)(?P<bracketBehind>])|"
128
+ "(?P<joinAhead>,)\\((?P<malkovich>\\d+),(?P<multiply>\\d+)\\)(?P<bracketBehind>])|"
129
129
  # Pattern 2: Bracket or start ahead, comma behind # noqa: ERA001
130
- "(?P<bracketOrStartAhead>\\[|^.)\\((?P<malkovichmalkovich>\\d+),(?P<multiple_fml>\\d+)\\)(?P<joinBehind>,)|"
130
+ "(?P<bracketOrStartAhead>\\[|^.)\\((?P<malkovichMalkovich>\\d+),(?P<multiplyIDK>\\d+)\\)(?P<joinBehind>,)|"
131
131
  # Pattern 3: Bracket ahead, bracket behind # noqa: ERA001
132
- "(?P<bracketAhead>\\[)\\((?P<malkovichmalkovichmalkovich>\\d+),(?P<multiple_whatever>\\d+)\\)(?P<bracketBehindbracketBehind>])|"
132
+ "(?P<bracketAhead>\\[)\\((?P<malkovichMalkovichMalkovich>\\d+),(?P<multiply_whatever>\\d+)\\)(?P<bracketBehindBracketBehind>])|"
133
133
  # Pattern 4: Comma ahead, comma behind # noqa: ERA001
134
- "(?P<joinAhead_prayharder>,)\\((?P<malkovichmalkovichmalkovichmalkovich>\\d+),(?P<multiple_prayharder>\\d+)\\)(?P<joinBehind_prayharder>,)"
134
+ "(?P<joinAheadJoinAhead>,)\\((?P<malkovichMalkovichMalkovichMalkovich>\\d+),(?P<multiplyOrSomething>\\d+)\\)(?P<joinBehindJoinBehind>,)"
135
135
  ")"
136
136
  )
137
137
 
138
138
  def replacementByContext(match: regex.Match[str]) -> str:
139
139
  """Generate replacement string based on context patterns."""
140
- yourIdentifiersSuck = match.groupdict()
141
- joinAhead = yourIdentifiersSuck.get('joinAhead') or yourIdentifiersSuck.get('joinAhead_prayharder')
142
- malkovich = yourIdentifiersSuck.get('malkovich') or yourIdentifiersSuck.get('malkovichmalkovich') or yourIdentifiersSuck.get('malkovichmalkovichmalkovich') or yourIdentifiersSuck.get('malkovichmalkovichmalkovichmalkovich')
143
- multiple = yourIdentifiersSuck.get('multiple') or yourIdentifiersSuck.get('multiple_fml') or yourIdentifiersSuck.get('multiple_whatever') or yourIdentifiersSuck.get('multiple_prayharder')
144
- joinBehind = yourIdentifiersSuck.get('joinBehind') or yourIdentifiersSuck.get('joinBehind_prayharder')
140
+ elephino = match.groupdict()
141
+ joinAhead = elephino.get('joinAhead') or elephino.get('joinAheadJoinAhead')
142
+ malkovich = elephino.get('malkovich') or elephino.get('malkovichMalkovich') or elephino.get('malkovichMalkovichMalkovich') or elephino.get('malkovichMalkovichMalkovichMalkovich')
143
+ multiply = elephino.get('multiply') or elephino.get('multiplyIDK') or elephino.get('multiply_whatever') or elephino.get('multiplyOrSomething')
144
+ joinBehind = elephino.get('joinBehind') or elephino.get('joinBehindJoinBehind')
145
145
 
146
146
  replaceAhead = "]+[" if joinAhead == "," else "["
147
147
 
148
148
  replaceBehind = "+[" if joinBehind == "," else ""
149
149
 
150
- return f"{replaceAhead}{malkovich}]*{multiple}{replaceBehind}"
150
+ return f"{replaceAhead}{malkovich}]*{multiply}{replaceBehind}"
151
151
 
152
152
  arrayAsStr = patternRegex.sub(replacementByContext, arrayAsStr)
153
153
  arrayAsStr = patternRegex.sub(replacementByContext, arrayAsStr)
@@ -249,7 +249,7 @@ def updateExtendPolishDictionaryLists(*dictionaryLists: Mapping[str, list[Any] |
249
249
  ImaStr = str(keyName)
250
250
  ImaList = list(keyValue)
251
251
  ePluribusUnum.setdefault(ImaStr, []).extend(ImaList)
252
- except TypeError: # noqa: PERF203
252
+ except TypeError:
253
253
  if killErroneousDataTypes:
254
254
  continue
255
255
  else:
@@ -1,7 +1,6 @@
1
1
  """File system and module import utilities.
2
2
 
3
- This module provides basic file I/O utilities such as writing tabular data to files, computing canonical relative paths, importing
4
- callables from modules, and safely creating directories.
3
+ This module provides basic file I/O utilities such as importing callables from modules, and safely creating directories.
5
4
 
6
5
  """
7
6
 
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: hunterMakesPy
3
+ Version: 0.1.2
4
+ Summary: Easy Python functions making making functional Python functions easier.
5
+ Author-email: Hunter Hogan <HunterHogan@pm.me>
6
+ License: CC-BY-NC-4.0
7
+ Project-URL: Donate, https://www.patreon.com/integrated
8
+ Project-URL: Homepage, https://github.com/hunterhogan/
9
+ Project-URL: Issues, https://github.com/hunterhogan/
10
+ Project-URL: Repository, https://github.com/hunterhogan/
11
+ Keywords: attribute loading,concurrency limit,configuration,defensive programming,dictionary merging,directory creation,dynamic import,error propagation,file system utilities,input validation,integer parsing,module loading,nested data structures,package settings,parameter validation,pytest,string extraction,test utilities
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Framework :: Pytest
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Intended Audience :: Other Audience
18
+ Classifier: Natural Language :: English
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: Implementation :: CPython
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Topic :: Utilities
28
+ Classifier: Typing :: Typed
29
+ Requires-Python: >=3.11
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Requires-Dist: charset_normalizer
33
+ Requires-Dist: more_itertools
34
+ Requires-Dist: numpy
35
+ Requires-Dist: python_minifier
36
+ Provides-Extra: development
37
+ Requires-Dist: mypy; extra == "development"
38
+ Requires-Dist: pyupgrade; extra == "development"
39
+ Requires-Dist: setuptools-scm; extra == "development"
40
+ Provides-Extra: testing
41
+ Requires-Dist: pytest; extra == "testing"
42
+ Requires-Dist: pytest-cov; extra == "testing"
43
+ Requires-Dist: pytest-xdist; extra == "testing"
44
+ Dynamic: license-file
45
+
46
+ # hunterMakesPy
47
+
48
+ A modular Python toolkit for defensive programming, parameter validation, file system utilities, and flexible data structure manipulation.
49
+
50
+ [![pip install hunterMakesPy](https://img.shields.io/badge/pip%20install-hunterMakesPy-gray.svg?colorB=3b434b)](https://pypi.org/project/hunterMakesPy/)
51
+
52
+ ## Overview
53
+
54
+ hunterMakesPy provides utilities for safe error handling, flexible input validation, dynamic module and attribute importing, and merging or transforming complex data structures. The package emphasizes clear identifiers, robust type handling, and reusable components for building reliable Python applications.
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ pip install hunterMakesPy
60
+ ```
61
+
62
+ ## Defensive Programming
63
+
64
+ Utilities for handling `None` values and defensive programming patterns.
65
+
66
+ ```python
67
+ from hunterMakesPy import raiseIfNone
68
+
69
+ # Ensure a function result is not None
70
+ def findConfiguration(configName: str) -> dict[str, str] | None:
71
+ # ... search logic ...
72
+ return None
73
+
74
+ config = raiseIfNone(
75
+ findConfiguration("database"),
76
+ "Configuration 'database' is required but not found"
77
+ )
78
+ ```
79
+
80
+ ## Parameter Validation
81
+
82
+ Parameter validation, integer parsing, and concurrency handling.
83
+
84
+ ```python
85
+ from hunterMakesPy import defineConcurrencyLimit, intInnit, oopsieKwargsie
86
+
87
+ # Smart concurrency limit calculation
88
+ cpuLimit = defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
89
+ cpuLimit = defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
90
+ cpuLimit = defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
91
+
92
+ # Robust integer validation
93
+ validatedIntegers = intInnit([1, "2", 3.0, "4"], "port_numbers")
94
+
95
+ # String-to-boolean conversion for configuration
96
+ userInput = "True"
97
+ booleanValue = oopsieKwargsie(userInput) # Returns True
98
+ ```
99
+
100
+ ## File System Utilities
101
+
102
+ Safe file operations and dynamic module importing.
103
+
104
+ ```python
105
+ from hunterMakesPy import (
106
+ importLogicalPath2Identifier,
107
+ importPathFilename2Identifier,
108
+ makeDirsSafely,
109
+ writeStringToHere
110
+ )
111
+
112
+ # Dynamic imports
113
+ gcdFunction = importLogicalPath2Identifier("math", "gcd")
114
+ customFunction = importPathFilename2Identifier("path/to/module.py", "functionName")
115
+
116
+ # Safe file operations
117
+ pathFilename = Path("deep/nested/directory/file.txt")
118
+ writeStringToHere("content", pathFilename) # Creates directories automatically
119
+ ```
120
+
121
+ ## Data Structure Manipulation
122
+
123
+ Utilities for string extraction, data flattening, and array compression.
124
+
125
+ ```python
126
+ from hunterMakesPy import stringItUp, updateExtendPolishDictionaryLists, autoDecodingRLE
127
+ import numpy
128
+
129
+ # Extract all strings from nested data structures
130
+ nestedData = {"config": [1, "host", {"port": 8080}], "users": ["alice", "bob"]}
131
+ allStrings = stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
132
+
133
+ # Merge dictionaries containing lists
134
+ dictionaryAlpha = {"servers": ["web1", "web2"], "databases": ["db1"]}
135
+ dictionaryBeta = {"servers": ["web3"], "databases": ["db2", "db3"]}
136
+ merged = updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
137
+
138
+ # Compress NumPy arrays with run-length encoding
139
+ arrayData = numpy.array([1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9])
140
+ compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
141
+ ```
142
+
143
+ ## Testing
144
+
145
+ The package includes comprehensive test suites that you can import and run:
146
+
147
+ ```python
148
+ from hunterMakesPy.pytestForYourUse import (
149
+ PytestFor_defineConcurrencyLimit,
150
+ PytestFor_intInnit,
151
+ PytestFor_oopsieKwargsie
152
+ )
153
+
154
+ # Run tests on the built-in functions
155
+ listOfTests = PytestFor_defineConcurrencyLimit()
156
+ for nameOfTest, callablePytest in listOfTests:
157
+ callablePytest()
158
+
159
+ # Or test your own compatible functions
160
+ @pytest.mark.parametrize("nameOfTest,callablePytest",
161
+ PytestFor_intInnit(callableToTest=myFunction))
162
+ def test_myFunction(nameOfTest, callablePytest):
163
+ callablePytest()
164
+ ```
165
+
166
+ ## My recovery
167
+
168
+ [![Static Badge](https://img.shields.io/badge/2011_August-Homeless_since-blue?style=flat)](https://HunterThinks.com/support)
169
+ [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UC3Gx7kz61009NbhpRtPP7tw)](https://www.youtube.com/@HunterHogan)
170
+
171
+ ## How to code
172
+
173
+ Coding One Step at a Time:
174
+
175
+ 0. WRITE CODE.
176
+ 1. Don't write stupid code that's hard to revise.
177
+ 2. Write good code.
178
+ 3. When revising, write better code.
179
+
180
+ [![CC-BY-NC-4.0](https://github.com/hunterhogan/hunterMakesPy/blob/main/CC-BY-NC-4.0.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
@@ -0,0 +1,20 @@
1
+ hunterMakesPy/__init__.py,sha256=4gIm--UJmlQEhbLCqpiVFM_JVbyfVpvFFAJyMNFHekk,1329
2
+ hunterMakesPy/_theSSOT.py,sha256=lkLOG3oTIWNKD_ULX55chlUGNqCHgqVIrBvolvK1vbQ,153
3
+ hunterMakesPy/coping.py,sha256=covqNFAwkF9gjafrlAvMdtCO8haFsESQBhO-7s68qSg,5581
4
+ hunterMakesPy/dataStructures.py,sha256=znMEnboo2iNXZtQKdmruRrJDEoeTfqoH4-coUDM75g4,11218
5
+ hunterMakesPy/filesystemToolkit.py,sha256=vDYS0Rc1aP7ETLXHKoO3TfOpuwJQFQW2ybwtJNiIIo4,4305
6
+ hunterMakesPy/parseParameters.py,sha256=plrJ4xR1FQnQR9j-oeMAwB2H9r_8QD8OALnw0OH8Kt0,11947
7
+ hunterMakesPy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ hunterMakesPy/pytestForYourUse.py,sha256=21BI8TXyinf8rMmS6dTsuRMBOOFTcyoG_gD__Gz_e7Q,13288
9
+ hunterMakesPy/theTypes.py,sha256=C2d0uLn1VIx6_2CK41it3IP7iplSQqe51tzWc-RT320,306
10
+ huntermakespy-0.1.2.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
11
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ tests/conftest.py,sha256=MkNc4Ar6yYrnAok4t1NWsgyOYFDiGo1qwXHyp8ChJf4,1672
13
+ tests/test_coping.py,sha256=MQl0fhdWX2YSFzzjWMThSm_ZSnglE3anSJO2b33typU,9846
14
+ tests/test_dataStructures.py,sha256=VhM3VzG1l8l3Iz1q9sQjpNBQTWSrl4zWbnefsGTccos,16406
15
+ tests/test_filesystemToolkit.py,sha256=jk6Ke0fW6dlHIVyvA1lh7o7_I7DVZr6uMO3s_i43isQ,2445
16
+ tests/test_parseParameters.py,sha256=bHD-O-OyWWwPOx3zvS53pSNPDm3LbFS05N5JcfNCLG4,1137
17
+ huntermakespy-0.1.2.dist-info/METADATA,sha256=b1Xr8jhpu8t-H3BTVQUcCww_gG8q5fcb1REz-8AQbY8,6555
18
+ huntermakespy-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ huntermakespy-0.1.2.dist-info/top_level.txt,sha256=dUy7z3LNO6aqNjPD81tZjE5N9HO70a14Y9lAWjWC2gA,20
20
+ huntermakespy-0.1.2.dist-info/RECORD,,
tests/test_coping.py CHANGED
@@ -1,4 +1,6 @@
1
- from hunterMakesPy import raiseIfNone
1
+ from hunterMakesPy import PackageSettings, raiseIfNone
2
+ from hunterMakesPy.coping import getIdentifierPackagePACKAGING, getPathPackageINSTALLING
3
+ from pathlib import Path
2
4
  from tests.conftest import uniformTestFailureMessage
3
5
  import pytest
4
6
 
@@ -54,3 +56,161 @@ def testRaiseIfNonePreservesTypeAnnotations() -> None:
54
56
 
55
57
  listValue: list[int] = raiseIfNone([29, 31])
56
58
  assert isinstance(listValue, list), uniformTestFailureMessage(list, type(listValue), "testRaiseIfNonePreservesTypeAnnotations", listValue)
59
+
60
+ # Tests for PackageSettings dataclass
61
+ @pytest.mark.parametrize(
62
+ "identifierPackageFALLBACK, expectedIdentifierPackage",
63
+ [
64
+ ("astToolFactory", "hunterMakesPy"), # Should read from pyproject.toml
65
+ ("nonExistentPackage", "hunterMakesPy"), # Should read from pyproject.toml
66
+ ("customPackage", "hunterMakesPy"), # Should read from pyproject.toml
67
+ ]
68
+ )
69
+ def testPackageSettingsWithFallbackUsesProjectToml(identifierPackageFALLBACK: str, expectedIdentifierPackage: str) -> None:
70
+ """Test that PackageSettings reads package name from pyproject.toml when using fallback."""
71
+ packageSettings = PackageSettings(identifierPackageFALLBACK)
72
+ assert packageSettings.identifierPackage == expectedIdentifierPackage, uniformTestFailureMessage(
73
+ expectedIdentifierPackage, packageSettings.identifierPackage, "PackageSettings fallback", identifierPackageFALLBACK
74
+ )
75
+
76
+ @pytest.mark.parametrize(
77
+ "explicitIdentifierPackage, expectedIdentifierPackage",
78
+ [
79
+ ("customPackageName", "customPackageName"),
80
+ ("fibonacci", "fibonacci"),
81
+ ("prime", "prime"),
82
+ ("astToolFactory", "astToolFactory"),
83
+ ]
84
+ )
85
+ def testPackageSettingsWithExplicitIdentifierPackage(explicitIdentifierPackage: str, expectedIdentifierPackage: str) -> None:
86
+ """Test that PackageSettings respects explicitly provided identifierPackage."""
87
+ packageSettings = PackageSettings(identifierPackage=explicitIdentifierPackage)
88
+ assert packageSettings.identifierPackage == expectedIdentifierPackage, uniformTestFailureMessage(
89
+ expectedIdentifierPackage, packageSettings.identifierPackage, "PackageSettings explicit identifierPackage", explicitIdentifierPackage
90
+ )
91
+
92
+ @pytest.mark.parametrize(
93
+ "explicitPathPackage, expectedPathPackage",
94
+ [
95
+ (Path("C:/fibonacci/path"), Path("C:/fibonacci/path")),
96
+ (Path("C:/prime/directory"), Path("C:/prime/directory")),
97
+ (Path("/usr/local/lib/package"), Path("/usr/local/lib/package")),
98
+ (Path("relative/path"), Path("relative/path")),
99
+ ]
100
+ )
101
+ def testPackageSettingsWithExplicitPathPackage(explicitPathPackage: Path, expectedPathPackage: Path) -> None:
102
+ """Test that PackageSettings respects explicitly provided pathPackage."""
103
+ packageSettings = PackageSettings(pathPackage=explicitPathPackage)
104
+ assert packageSettings.pathPackage == expectedPathPackage, uniformTestFailureMessage(
105
+ expectedPathPackage, packageSettings.pathPackage, "PackageSettings explicit pathPackage", explicitPathPackage
106
+ )
107
+
108
+ @pytest.mark.parametrize(
109
+ "fileExtension, expectedFileExtension",
110
+ [
111
+ (".fibonacci", ".fibonacci"),
112
+ (".prime", ".prime"),
113
+ (".txt", ".txt"),
114
+ (".md", ".md"),
115
+ (".json", ".json"),
116
+ ]
117
+ )
118
+ def testPackageSettingsWithCustomFileExtension(fileExtension: str, expectedFileExtension: str) -> None:
119
+ """Test that PackageSettings respects custom file extensions."""
120
+ packageSettings = PackageSettings(fileExtension=fileExtension)
121
+ assert packageSettings.fileExtension == expectedFileExtension, uniformTestFailureMessage(
122
+ expectedFileExtension, packageSettings.fileExtension, "PackageSettings custom fileExtension", fileExtension
123
+ )
124
+
125
+ def testPackageSettingsDefaultValues() -> None:
126
+ """Test that PackageSettings has correct default values when no arguments provided."""
127
+ packageSettings = PackageSettings()
128
+
129
+ # Should have default file extension
130
+ assert packageSettings.fileExtension == '.py', uniformTestFailureMessage(
131
+ '.py', packageSettings.fileExtension, "PackageSettings default fileExtension"
132
+ )
133
+
134
+ # identifierPackage should be empty when no fallback provided
135
+ assert packageSettings.identifierPackage == '', uniformTestFailureMessage(
136
+ '', packageSettings.identifierPackage, "PackageSettings default identifierPackage"
137
+ )
138
+
139
+ # pathPackage should remain as Path() when identifierPackage is empty
140
+ expectedPath = Path()
141
+ assert packageSettings.pathPackage == expectedPath, uniformTestFailureMessage(
142
+ expectedPath, packageSettings.pathPackage, "PackageSettings default pathPackage"
143
+ )
144
+
145
+ @pytest.mark.parametrize(
146
+ "identifierPackageFALLBACK, identifierPackage, pathPackage, fileExtension",
147
+ [
148
+ ("fallback", "custom", Path("C:/custom/path"), ".txt"),
149
+ ("fibonacci", "prime", Path("C:/fibonacci/lib"), ".md"),
150
+ ("defaultFallback", "overridePackage", Path("/usr/local/override"), ".json"),
151
+ ]
152
+ )
153
+ def testPackageSettingsAllParametersCombined(
154
+ identifierPackageFALLBACK: str,
155
+ identifierPackage: str,
156
+ pathPackage: Path,
157
+ fileExtension: str
158
+ ) -> None:
159
+ """Test PackageSettings with all parameters provided."""
160
+ packageSettings = PackageSettings(
161
+ identifierPackageFALLBACK,
162
+ identifierPackage=identifierPackage,
163
+ pathPackage=pathPackage,
164
+ fileExtension=fileExtension
165
+ )
166
+
167
+ assert packageSettings.identifierPackage == identifierPackage, uniformTestFailureMessage(
168
+ identifierPackage, packageSettings.identifierPackage, "PackageSettings combined identifierPackage"
169
+ )
170
+ assert packageSettings.pathPackage == pathPackage, uniformTestFailureMessage(
171
+ pathPackage, packageSettings.pathPackage, "PackageSettings combined pathPackage"
172
+ )
173
+ assert packageSettings.fileExtension == fileExtension, uniformTestFailureMessage(
174
+ fileExtension, packageSettings.fileExtension, "PackageSettings combined fileExtension"
175
+ )
176
+
177
+ def testPackageSettingsFallbackIgnoredWhenExplicitIdentifierProvided() -> None:
178
+ """Test that fallback is ignored when explicit identifierPackage is provided."""
179
+ packageSettings = PackageSettings("shouldBeIgnored", identifierPackage="explicit")
180
+ assert packageSettings.identifierPackage == "explicit", uniformTestFailureMessage(
181
+ "explicit", packageSettings.identifierPackage, "PackageSettings fallback ignored"
182
+ )
183
+
184
+ # Tests for helper functions
185
+ @pytest.mark.parametrize(
186
+ "identifierPackageFALLBACK, expectedResult",
187
+ [
188
+ ("fibonacci", "hunterMakesPy"), # Should read from pyproject.toml
189
+ ("prime", "hunterMakesPy"), # Should read from pyproject.toml
190
+ ("nonExistentPackage", "hunterMakesPy"), # Should read from pyproject.toml
191
+ ]
192
+ )
193
+ def testGetIdentifierPackagePACKAGING(identifierPackageFALLBACK: str, expectedResult: str) -> None:
194
+ """Test that getIdentifierPackagePACKAGING reads from pyproject.toml correctly."""
195
+ actual = getIdentifierPackagePACKAGING(identifierPackageFALLBACK)
196
+ assert actual == expectedResult, uniformTestFailureMessage(
197
+ expectedResult, actual, "getIdentifierPackagePACKAGING", identifierPackageFALLBACK
198
+ )
199
+
200
+ @pytest.mark.parametrize(
201
+ "identifierPackage",
202
+ [
203
+ "hunterMakesPy", # This package exists
204
+ "fibonacci", # Non-existent package should fallback to cwd
205
+ "prime", # Non-existent package should fallback to cwd
206
+ ]
207
+ )
208
+ def testGetPathPackageINSTALLING(identifierPackage: str) -> None:
209
+ """Test that getPathPackageINSTALLING returns valid Path objects."""
210
+ actual = getPathPackageINSTALLING(identifierPackage)
211
+ assert isinstance(actual, Path), uniformTestFailureMessage(
212
+ Path, type(actual), "getPathPackageINSTALLING type", identifierPackage
213
+ )
214
+ assert actual.exists() or actual == Path.cwd(), uniformTestFailureMessage(
215
+ "existing path or cwd", actual, "getPathPackageINSTALLING existence", identifierPackage
216
+ )
@@ -22,7 +22,7 @@ def testWriteStringToHereCreatesFileAndWritesContent(pathTmpTesting: pathlib.Pat
22
22
  filePath = nestedDirectory / "test.txt"
23
23
  writeStringToHere("hello world", filePath)
24
24
  assert filePath.exists(), uniformTestFailureMessage(True, filePath.exists(), "testWriteStringToHereCreatesFileAndWritesContent", filePath)
25
- assert filePath.read_text() == "hello world", uniformTestFailureMessage("hello world", filePath.read_text(), "testWriteStringToHereCreatesFileAndWritesContent", filePath)
25
+ assert filePath.read_text(encoding="utf-8") == "hello world", uniformTestFailureMessage("hello world", filePath.read_text(encoding="utf-8"), "testWriteStringToHereCreatesFileAndWritesContent", filePath)
26
26
 
27
27
  @pytest.mark.parametrize(
28
28
  "moduleName, identifier, expectedType",
@@ -1,38 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: hunterMakesPy
3
- Version: 0.1.0
4
- Author-email: Hunter Hogan <HunterHogan@pm.me>
5
- License: CC-BY-NC-4.0
6
- Project-URL: Donate, https://www.patreon.com/integrated
7
- Project-URL: Homepage, https://github.com/hunterhogan/
8
- Project-URL: Issues, https://github.com/hunterhogan/
9
- Project-URL: Repository, https://github.com/hunterhogan/
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Environment :: Console
12
- Classifier: Intended Audience :: End Users/Desktop
13
- Classifier: Intended Audience :: Other Audience
14
- Classifier: Natural Language :: English
15
- Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python
17
- Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
- Classifier: Typing :: Typed
23
- Requires-Python: >=3.10
24
- Description-Content-Type: text/markdown
25
- License-File: LICENSE
26
- Requires-Dist: charset_normalizer
27
- Requires-Dist: more_itertools
28
- Requires-Dist: numpy
29
- Requires-Dist: python_minifier
30
- Requires-Dist: tomli
31
- Provides-Extra: testing
32
- Requires-Dist: mypy; extra == "testing"
33
- Requires-Dist: pytest; extra == "testing"
34
- Requires-Dist: pytest-cov; extra == "testing"
35
- Requires-Dist: pytest-xdist; extra == "testing"
36
- Requires-Dist: pyupgrade; extra == "testing"
37
- Requires-Dist: setuptools-scm; extra == "testing"
38
- Dynamic: license-file
@@ -1,20 +0,0 @@
1
- hunterMakesPy/__init__.py,sha256=0zTe--KMtLeoBfsKGS5hH_vUEOuOXRQt1nA-K25bMBw,816
2
- hunterMakesPy/_theSSOT.py,sha256=HiZ_OqaiiHBDdbyKLJPk7aARQujShdX3dpYEpUbpWbM,1631
3
- hunterMakesPy/coping.py,sha256=AXa3KgAAO0L3KFNUoor8Tjuw7lDGQ2ZIsKUCDV4wR2Y,2529
4
- hunterMakesPy/dataStructures.py,sha256=2mJDAOxgSstV0gDNcpwA0mHruDbAfeRGIADWTCPXuHY,11397
5
- hunterMakesPy/filesystemToolkit.py,sha256=OdbMKPuOvverjptsToENU2HNGZ2XzAE7Bm6i0-yJfiU,4372
6
- hunterMakesPy/parseParameters.py,sha256=plrJ4xR1FQnQR9j-oeMAwB2H9r_8QD8OALnw0OH8Kt0,11947
7
- hunterMakesPy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- hunterMakesPy/pytestForYourUse.py,sha256=21BI8TXyinf8rMmS6dTsuRMBOOFTcyoG_gD__Gz_e7Q,13288
9
- hunterMakesPy/theTypes.py,sha256=C2d0uLn1VIx6_2CK41it3IP7iplSQqe51tzWc-RT320,306
10
- huntermakespy-0.1.0.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
11
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- tests/conftest.py,sha256=MkNc4Ar6yYrnAok4t1NWsgyOYFDiGo1qwXHyp8ChJf4,1672
13
- tests/test_coping.py,sha256=c_rBHU0uoK2CKZhJSWtp7PPnHgdjNtdQEbWpc4P0VkE,2282
14
- tests/test_dataStructures.py,sha256=VhM3VzG1l8l3Iz1q9sQjpNBQTWSrl4zWbnefsGTccos,16406
15
- tests/test_filesystemToolkit.py,sha256=aMPyJ-EUCk4XvA08BSC5nDqKPY-YFBM_ePMGRZlSZOo,2413
16
- tests/test_parseParameters.py,sha256=bHD-O-OyWWwPOx3zvS53pSNPDm3LbFS05N5JcfNCLG4,1137
17
- huntermakespy-0.1.0.dist-info/METADATA,sha256=YPPLNpMrsIdnhqTuSNdIzEHB3-ckuMsmQEHy5hPSGyc,1474
18
- huntermakespy-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- huntermakespy-0.1.0.dist-info/top_level.txt,sha256=dUy7z3LNO6aqNjPD81tZjE5N9HO70a14Y9lAWjWC2gA,20
20
- huntermakespy-0.1.0.dist-info/RECORD,,