Skip to content

Commit 19a0164

Browse files
committed
v1.1.4 - Added env_bool function, csv/kval functions now allow separator to be overriden.
**New functions / code improvements** - Added `env_bool` function to `common.py` for loading an environment var as a boolean - Added `csvsplit` parameter to parse/env_csv and keyval, to allow changing the item separator from `,` - Added `valsplit` parameter to parse/env_keyval, to allow customising the separator between key's and value's from `:` **Documentation** - Added PyDoc param's / return's for various functions, and fleshed out some others - Wrapped various docstring values such as ``True`` and ``0`` with backticks so they display better - Various small formatting improvements to existing docstrings - Added `PrivexBaseCase` and `env_bool` to the docs **Unit Testing** - Refactored various attributes in `test.py` into the base class `PrivexBaseClass` - Added example to the PyDoc in `tests.py` showing how to run tests with `pytest` - Wrote new unit tests: - `test_kval_custom_clean` - Validates that `parse_keyval` works properly with custom `valsplit`/`csvsplit` - `test_kval_custom_spaced` - Validates that `parse_keyval` works properly with space padded values and custom `valsplit`/`csvsplit` - `test_env_nonexist_bool`, `test_env_bool_true`, `test_env_bool_false` - Validate that the new `env_bool` function returns the correct values.
1 parent aa75feb commit 19a0164

File tree

7 files changed

+273
-47
lines changed

7 files changed

+273
-47
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
privex.helpers.common.env\_bool
2+
===============================
3+
4+
.. currentmodule:: privex.helpers.common
5+
6+
.. autofunction:: env_bool

docs/source/helpers/privex.helpers.common.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ privex.helpers.common
1313
empty
1414
env_csv
1515
env_keyval
16+
env_bool
1617
is_false
1718
is_true
1819
parse_csv
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
tests.PrivexBaseCase
2+
====================
3+
4+
.. currentmodule:: tests
5+
6+
.. autoclass:: PrivexBaseCase
7+
8+
9+
.. automethod:: __init__
10+
11+
12+
.. rubric:: Methods
13+
14+
.. autosummary::
15+
16+
~PrivexBaseCase.__init__
17+
~PrivexBaseCase.addCleanup
18+
~PrivexBaseCase.addTypeEqualityFunc
19+
~PrivexBaseCase.assertAlmostEqual
20+
~PrivexBaseCase.assertAlmostEquals
21+
~PrivexBaseCase.assertCountEqual
22+
~PrivexBaseCase.assertDictContainsSubset
23+
~PrivexBaseCase.assertDictEqual
24+
~PrivexBaseCase.assertEqual
25+
~PrivexBaseCase.assertEquals
26+
~PrivexBaseCase.assertFalse
27+
~PrivexBaseCase.assertGreater
28+
~PrivexBaseCase.assertGreaterEqual
29+
~PrivexBaseCase.assertIn
30+
~PrivexBaseCase.assertIs
31+
~PrivexBaseCase.assertIsInstance
32+
~PrivexBaseCase.assertIsNone
33+
~PrivexBaseCase.assertIsNot
34+
~PrivexBaseCase.assertIsNotNone
35+
~PrivexBaseCase.assertLess
36+
~PrivexBaseCase.assertLessEqual
37+
~PrivexBaseCase.assertListEqual
38+
~PrivexBaseCase.assertLogs
39+
~PrivexBaseCase.assertMultiLineEqual
40+
~PrivexBaseCase.assertNotAlmostEqual
41+
~PrivexBaseCase.assertNotAlmostEquals
42+
~PrivexBaseCase.assertNotEqual
43+
~PrivexBaseCase.assertNotEquals
44+
~PrivexBaseCase.assertNotIn
45+
~PrivexBaseCase.assertNotIsInstance
46+
~PrivexBaseCase.assertNotRegex
47+
~PrivexBaseCase.assertNotRegexpMatches
48+
~PrivexBaseCase.assertRaises
49+
~PrivexBaseCase.assertRaisesRegex
50+
~PrivexBaseCase.assertRaisesRegexp
51+
~PrivexBaseCase.assertRegex
52+
~PrivexBaseCase.assertRegexpMatches
53+
~PrivexBaseCase.assertSequenceEqual
54+
~PrivexBaseCase.assertSetEqual
55+
~PrivexBaseCase.assertTrue
56+
~PrivexBaseCase.assertTupleEqual
57+
~PrivexBaseCase.assertWarns
58+
~PrivexBaseCase.assertWarnsRegex
59+
~PrivexBaseCase.assert_
60+
~PrivexBaseCase.countTestCases
61+
~PrivexBaseCase.debug
62+
~PrivexBaseCase.defaultTestResult
63+
~PrivexBaseCase.doCleanups
64+
~PrivexBaseCase.fail
65+
~PrivexBaseCase.failIf
66+
~PrivexBaseCase.failIfAlmostEqual
67+
~PrivexBaseCase.failIfEqual
68+
~PrivexBaseCase.failUnless
69+
~PrivexBaseCase.failUnlessAlmostEqual
70+
~PrivexBaseCase.failUnlessEqual
71+
~PrivexBaseCase.failUnlessRaises
72+
~PrivexBaseCase.id
73+
~PrivexBaseCase.run
74+
~PrivexBaseCase.setUp
75+
~PrivexBaseCase.setUpClass
76+
~PrivexBaseCase.shortDescription
77+
~PrivexBaseCase.skipTest
78+
~PrivexBaseCase.subTest
79+
~PrivexBaseCase.tearDown
80+
~PrivexBaseCase.tearDownClass
81+
82+
83+
84+
85+
86+
.. rubric:: Attributes
87+
88+
.. autosummary::
89+
90+
~PrivexBaseCase.empty_lst
91+
~PrivexBaseCase.empty_vals
92+
~PrivexBaseCase.empty_zero
93+
~PrivexBaseCase.falsey
94+
~PrivexBaseCase.falsey_empty
95+
~PrivexBaseCase.longMessage
96+
~PrivexBaseCase.maxDiff
97+
~PrivexBaseCase.truthy
98+
99+

docs/source/helpers/tests.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ tests
1818
TestBoolHelpers
1919
TestIPReverseDNS
2020
TestParseHelpers
21+
PrivexBaseCase
2122

2223

2324

privex/helpers/common.py

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def random_str(size:int = 50, chars: Sequence = SAFE_CHARS) -> str:
5959
SystemRandom class to provide relatively secure randomness from the OS. (On Linux, uses /dev/urandom)
6060
6161
By default, uses the character set :py:attr:`.SAFE_CHARS` which contains letters a-z / A-Z and numbers 2-9
62-
with commonly misread characters removed (such as 1, l, L, 0 and o). Pass :py:attr:`.ALPHANUM` as `chars` if
63-
you needthe full set of upper/lowercase + numbers.
62+
with commonly misread characters removed (such as ``1``, ``l``, ``L``, ``0`` and ``o``). Pass
63+
:py:attr:`.ALPHANUM` as `chars` if you need the full set of upper/lowercase + numbers.
6464
6565
Usage:
6666
@@ -86,10 +86,10 @@ def random_str(size:int = 50, chars: Sequence = SAFE_CHARS) -> str:
8686

8787
def empty(v, zero: bool = False, itr: bool = False) -> bool:
8888
"""
89-
Quickly check if a variable is empty or not. By default only '' and None are checked, use `itr` and `zero` to
89+
Quickly check if a variable is empty or not. By default only '' and None are checked, use ``itr`` and ``zero`` to
9090
test for empty iterable's and zeroed variables.
9191
92-
Returns True if a variable is None or '', returns False if variable passes the tests
92+
Returns ``True`` if a variable is ``None`` or ``''``, returns ``False`` if variable passes the tests
9393
9494
Example usage:
9595
@@ -101,10 +101,10 @@ def empty(v, zero: bool = False, itr: bool = False) -> bool:
101101
... print('Var x is None, blank string, or an empty dict/list/iterable')
102102
103103
:param v: The variable to check if it's empty
104-
:param zero: if zero=True, then return True if the variable is int 0 or str '0'
105-
:param itr: if itr=True, then return True if the variable is ``[]``, ``{}``, or is an iterable and has 0 length
106-
:return bool is_blank: True if a variable is blank (``None``, ``''``, ``0``, ``[]`` etc.)
107-
:return bool is_blank: False if a variable has content (or couldn't be checked properly)
104+
:param zero: if ``zero=True``, then return ``True`` if the variable is int ``0`` or str ``'0'``
105+
:param itr: if ``itr=True``, then return ``True`` if the variable is ``[]``, ``{}``, or is an iterable and has 0 length
106+
:return bool is_blank: ``True`` if a variable is blank (``None``, ``''``, ``0``, ``[]`` etc.)
107+
:return bool is_blank: ``False`` if a variable has content (or couldn't be checked properly)
108108
"""
109109

110110
_check = [None, '']
@@ -118,8 +118,11 @@ def empty(v, zero: bool = False, itr: bool = False) -> bool:
118118

119119
def is_true(v) -> bool:
120120
"""
121-
Check if a given bool/str/int value is some form of True:
122-
boolean: True // string: 'true', 'yes', 'y', '1' // integer: 1
121+
Check if a given bool/str/int value is some form of ``True``:
122+
123+
* **bool**: ``True``
124+
* **str**: ``'true'``, ``'yes'``, ``'y'``, ``'1'``
125+
* **int**: ``1``
123126
124127
(note: strings are automatically .lower()'d)
125128
@@ -131,7 +134,7 @@ def is_true(v) -> bool:
131134
False
132135
133136
:param Any v: The value to check for truthfulness
134-
:return bool is_true: True if the value appears to be truthy, otherwise False.
137+
:return bool is_true: ``True`` if the value appears to be truthy, otherwise ``False``.
135138
"""
136139
v = v.lower() if type(v) is str else v
137140
return v in [True, 'true', 'yes', 'y', '1', 1]
@@ -141,9 +144,11 @@ def is_false(v, chk_none: bool = True) -> bool:
141144
**Warning:** Unless you specifically need to verify a value is Falsey, it's usually safer to
142145
check for truth :py:func:`.is_true` and invert the result, i.e. ``if not is_true(v)``
143146
144-
Check if a given bool/str/int value is some form of False::
147+
Check if a given bool/str/int value is some form of ``False``:
145148
146-
boolean: False // string: 'false', 'no', 'n', '0' // integer: 0
149+
* **bool**: ``False``
150+
* **str**: ``'false'``, ``'no'``, ``'n'``, ``'0'``
151+
* **int**: ``0``
147152
148153
If ``chk_none`` is True (default), will also consider the below values to be Falsey::
149154
@@ -159,15 +164,15 @@ def is_false(v, chk_none: bool = True) -> bool:
159164
False
160165
161166
:param Any v: The value to check for falseyness
162-
:param bool chk_none: If True, treat None/'none'/'null' as Falsey (default True)
163-
:return bool is_False: True if the value appears to be falsey, otherwise False.
167+
:param bool chk_none: If ``True``, treat ``None``/``'none'``/``'null'`` as Falsey (default ``True``)
168+
:return bool is_False: ``True`` if the value appears to be falsey, otherwise ``False``.
164169
"""
165170
v = v.lower() if type(v) is str else v
166171
chk = [False, 'false', 'no', 'n', '0', 0]
167172
chk += [None, 'none', 'null', ''] if chk_none else []
168173
return v in chk
169174

170-
def parse_keyval(line: str) -> List[Tuple[str, str]]:
175+
def parse_keyval(line: str, valsplit: str = ':', csvsplit=',') -> List[Tuple[str, str]]:
171176
"""
172177
Parses a csv with key:value pairs such as::
173178
@@ -182,22 +187,20 @@ def parse_keyval(line: str) -> List[Tuple[str, str]]:
182187
]
183188
184189
190+
By default, uses a colons ``:`` to split the key/value, and commas ``,`` to terminate the end of
191+
each keyval pair. This can be overridden by changing valsplit/csvsplit.
192+
185193
:param str line: A string of key:value pairs separated by commas e.g. ``John Alex:Doe,Jane Sarah:Doe``
194+
:param str valsplit: A character (or several) used to split the key from the value (default: colon ``:``)
195+
:param str csvsplit: A character (or several) used to terminate each keyval pair (default: comma ``,``)
186196
:return List[Tuple[str,str]] parsed_data: A list of (key, value) tuples that can easily be casted to a dict()
187197
"""
188-
line = [tuple(a.split(':')) for a in line.split(',')] if line != '' else []
198+
cs, vs = csvsplit, valsplit
199+
line = [tuple(a.split(vs)) for a in line.split(cs)] if line != '' else []
189200
return [(a.strip(), b.strip()) for a, b in line]
190201

191-
def env_keyval(env_key: str, env_default = None) -> List[Tuple[str, str]]:
192-
"""
193-
Parses ``key:val,key:val`` into a list of tuples [(key,val), (key,val)]
194-
195-
See :py:meth:`parse_keyval`
196-
"""
197-
d = env(env_key)
198-
return env_default if empty(d) else parse_keyval(d)
199202

200-
def parse_csv(line: str) -> List[str]:
203+
def parse_csv(line: str, csvsplit: str = ',') -> List[str]:
201204
"""
202205
Quick n' dirty parsing of a simple comma separated line, with automatic whitespace stripping
203206
of both the ``line`` itself, and the values within the commas.
@@ -206,11 +209,14 @@ def parse_csv(line: str) -> List[str]:
206209
207210
>>> parse_csv(' hello , world, test')
208211
['hello', 'world', 'test']
212+
>>> parse_csv(' world ; test ; example', csvsplit=';')
213+
['world', 'test', 'example']
209214
215+
:param str csvsplit: A character (or several) used to terminate each value in the list. Default: comma ``,``
210216
"""
211-
return [x.strip() for x in line.strip().split(',')]
217+
return [x.strip() for x in line.strip().split(csvsplit)]
212218

213-
def env_csv(env_key: str, env_default = None) -> List[str]:
219+
def env_csv(env_key: str, env_default = None, csvsplit=',') -> List[str]:
214220
"""
215221
Quick n' dirty parsing of simple CSV formatted environment variables, with fallback
216222
to user specified ``env_default`` (defaults to None)
@@ -224,11 +230,46 @@ def env_csv(env_key: str, env_default = None) -> List[str]:
224230
[]
225231
226232
:param str env_key: Environment var to attempt to load
227-
:param any env_default: Fallback value if the env var is empty / not set
233+
:param any env_default: Fallback value if the env var is empty / not set (Default: None)
234+
:param str csvsplit: A character (or several) used to terminate each value in the list. Default: comma ``,``
228235
:return List[str] parsed_data: A list of str values parsed from the env var
229236
"""
230237
d = env(env_key)
231-
return env_default if empty(d) else parse_csv(d)
238+
return env_default if empty(d) else parse_csv(d, csvsplit=csvsplit)
239+
240+
def env_keyval(env_key: str, env_default = None, valsplit=':', csvsplit=',') -> List[Tuple[str, str]]:
241+
"""
242+
Parses an environment variable containing ``key:val,key:val`` into a list of tuples [(key,val), (key,val)]
243+
244+
See :py:meth:`parse_keyval`
245+
246+
:param str env_key: Environment var to attempt to load
247+
:param any env_default: Fallback value if the env var is empty / not set (Default: None)
248+
:param str valsplit: A character (or several) used to split the key from the value (default: colon ``:``)
249+
:param str csvsplit: A character (or several) used to terminate each keyval pair (default: comma ``,``)
250+
"""
251+
d = env(env_key)
252+
return env_default if empty(d) else parse_keyval(d, valsplit=valsplit, csvsplit=csvsplit)
253+
254+
def env_bool(env_key: str, env_default = None) -> Union[bool, None]:
255+
"""
256+
Obtains an environment variable ``env_key``, if it's empty or not set, ``env_default`` will be returned.
257+
Otherwise, it will be converted into a boolean using :py:func:`.is_true`
258+
259+
Example:
260+
261+
>>> os.environ['HELLO_WORLD'] = '1'
262+
>>> env_bool('HELLO_WORLD')
263+
True
264+
>>> env_bool('HELLO_NOEXIST')
265+
None
266+
>>> env_bool('HELLO_NOEXIST', 'error')
267+
'error'
268+
269+
:param str env_key: Environment var to attempt to load
270+
:param any env_default: Fallback value if the env var is empty / not set (Default: None)
271+
"""
272+
return env_default if empty(env(env_key)) else is_true(env(env_key))
232273

233274

234275
class ErrHelpParser(argparse.ArgumentParser):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
setup(
4646
name='privex_helpers',
4747

48-
version='1.1.2',
48+
version='1.1.4',
4949

5050
description='A variety of helper functions and classes, useful for many different projects',
5151
long_description=long_description,

0 commit comments

Comments
 (0)