Skip to content

Commit

Permalink
Finalizing new version activities - refactorings, tests, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitry-ed-gusev committed Nov 25, 2022
1 parent 87cf38f commit 582dacf
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 70 deletions.
6 changes: 4 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ ignore_missing_imports = True

# -- flake8 library config
[flake8]
# - list of ignored issues (E501 - too long line)
#ignore = E501
# - list of ignored issues:
# E501 - too long line
# E203 - whitespace before ':' - black formatter puts it
ignore = E203
# - max cognitive complexity for statements
max-complexity = 10
# - adjustment to align with line length for black (it uses 88 chars by default)
Expand Down
5 changes: 3 additions & 2 deletions src/pyutilities/commands/pygit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
Some useful/convenient functions related to GIT.
Created: Dmitrii Gusev, 03.05.2019
Modified: Dmitrii Gusev, 14.10.2022
Modified: Dmitrii Gusev, 25.11.2022
"""

import logging
from subprocess import Popen
from pyutilities.utils.common_utils import myself
from pyutilities.exception import PyUtilitiesException
from pyutilities.defaults import MSG_MODULE_ISNT_RUNNABLE

log = logging.getLogger(__name__)
log.addHandler(logging.NullHandler())
Expand Down Expand Up @@ -101,4 +102,4 @@ class GitException(Exception):


if __name__ == "__main__":
print("pyutilities.pygit: Don't try to execute library as a standalone app!")
print(MSG_MODULE_ISNT_RUNNABLE)
7 changes: 6 additions & 1 deletion src/pyutilities/commands/pymaven.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Functions are incapsulated in PyMaven class.
Created: Dmitrii Gusev, 02.05.2019
Modified: Dmitrii Gusev, 24.11.2022
Modified: Dmitrii Gusev, 25.11.2022
"""

import os
Expand All @@ -16,6 +16,7 @@
from subprocess import Popen
from pyutilities.utils.common_utils import myself
from pyutilities.exception import PyUtilitiesException
from pyutilities.defaults import MSG_MODULE_ISNT_RUNNABLE

log = logging.getLogger(__name__)
log.addHandler(logging.NullHandler())
Expand Down Expand Up @@ -94,3 +95,7 @@ def sources(self, location):
raise PyUtilitiesException(f"Process returned non zero exit code [{process.returncode}]!")
except AttributeError as se:
log.error(f"Error downloading sources for repo [{location}]! {se}")


if __name__ == "__main__":
print(MSG_MODULE_ISNT_RUNNABLE)
21 changes: 7 additions & 14 deletions src/pyutilities/config/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
24.11.2022 Various refactorings, added some typing.
Created: Gusev Dmitrii, XX.08.2017
Modified: Gusev Dmitrii, 24.11.2022
Modified: Gusev Dmitrii, 25.11.2022
"""

import os
Expand All @@ -22,6 +22,7 @@

from string import Template
from pyutilities.io.io_utils import read_yaml
from pyutilities.defaults import MSG_MODULE_ISNT_RUNNABLE

YAML_EXTENSION_1 = ".yml"
YAML_EXTENSION_2 = ".yaml"
Expand Down Expand Up @@ -103,11 +104,9 @@ def load(self, path: str | None, is_merge_env=True):
# todo: extract two methods - load from file/load from dir + refactor unit tests
# if provided path to single file - load it, otherwise - load from directory
if os.path.isfile(path) and (path.endswith(YAML_EXTENSION_1) or path.endswith(YAML_EXTENSION_2)):
self.log.debug("Provided path [{}] is a YAML file. Loading.".format(path))
try:
self.merge_dict(read_yaml(path))
except ConfigError as ex:
raise ConfigError("ERROR while merging file %s to configuration.\n%s" % (path, ex))
self.log.debug("Provided path [{}] is a YAML file.".format(path))
self.log.debug("Loading configuration from [{}].".format(path))
self.merge_dict(read_yaml(path))

# loading from directory (all YAML files)
elif os.path.isdir(path):
Expand All @@ -118,12 +117,7 @@ def load(self, path: str | None, is_merge_env=True):
some_file.endswith(YAML_EXTENSION_1) or some_file.endswith(YAML_EXTENSION_2)
):
self.log.debug("Loading configuration from [{}].".format(some_file))
try:
self.merge_dict(read_yaml(file_path))
except ConfigError as ex:
raise ConfigError(
"ERROR while merging file %s to configuration.\n%s" % (file_path, ex)
)
self.merge_dict(read_yaml(file_path))

# unknown file/dir type
else:
Expand All @@ -148,7 +142,6 @@ def merge_dict(self, new_dict):
self.config_dict = result
else:
self.config_dict.update(new_dict)
return

def __add_entity__(self, dict1, dict2, current_key=""):
"""Adds second dictionary to the first (processing nested dicts recursively)
Expand Down Expand Up @@ -350,4 +343,4 @@ def load_dict_from_xls(self, path_to_xls, config_sheet_name):


if __name__ == "__main__":
print("pyutilities.config: Don't try to execute library as a standalone app!")
print(MSG_MODULE_ISNT_RUNNABLE)
43 changes: 13 additions & 30 deletions src/pyutilities/utils/string_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
to module String in java library Apache Commons).
Created: Dmitrii Gusev, 15.04.2019
Modified: Dmitrii Gusev, 24.11.2022
Modified: Dmitrii Gusev, 25.11.2022
"""

import logging
from typing import Tuple, Dict, AnyStr
from typing import Tuple, Dict
from pyutilities.exception import PyUtilitiesException
from pyutilities.defaults import MSG_MODULE_ISNT_RUNNABLE

Expand All @@ -21,37 +21,20 @@
log.addHandler(logging.NullHandler())


def is_str_empty(string: AnyStr | None) -> bool:
"""Check is string empty/NoNe or not.
:param string:
:return:
"""
if string is None or not string or not string.strip(): # check for whitespaces string
return True

return False # all checks passed


def trim_to_none(string: str):
"""Trim the provided string to None (if empty) or just strip whitespaces.
:param string:
:return:
"""
if is_str_empty(string): # check for empty string
return None
def trim_to_none(string: str | None) -> str | None:
"""Trim the provided string to None (if empty) or just strip whitespaces."""
if string and string.strip():
return string.strip()

return string.strip() # strip and return
return None


def trim_to_empty(string: str) -> str:
"""Trim the provided string to empty string (''/"") or just strip whitespaces.
:param string:
:return:
"""
if is_str_empty(string): # check for empty string
return ""
def trim_to_empty(string: str | None) -> str:
"""Trim the provided string to empty string ('' or "") or just strip whitespaces."""
if string and string.strip():
return string.strip()

return string.strip()
return ""


def filter_str(string): # todo: fix filtering for non-cyrillic symbols too (add them)
Expand Down Expand Up @@ -110,7 +93,7 @@ def get_last_part_of_the_url(url: str) -> str:
if not url: # fail-fast behaviour
raise PyUtilitiesException("Specified empty URL!")

return url[url.rfind("/") + 1:]
return url[url.rfind("/") + 1 :]


if __name__ == "__main__":
Expand Down
5 changes: 2 additions & 3 deletions src/pyutilities/web/web_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- (HTTP status codes) https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
Created: Dmitrii Gusev, 10.10.2022
Modified: Dmitrii Gusev, 22.11.2022
Modified: Dmitrii Gusev, 25.11.2022
"""

import logging
Expand All @@ -27,9 +27,8 @@
from pyutilities.exception import PyUtilitiesException
from pyutilities.defaults import MSG_MODULE_ISNT_RUNNABLE

# init module logger
log = logging.getLogger(__name__)
log.debug(f"Logging for module {__name__} is configured.")
log.addHandler(logging.NullHandler())

HTTP_DEFAULT_TIMEOUT = 20 # default HTTP requests timeout (seconds)
HTTP_DEFAULT_BACKOFF = 1 # default back off factor (it is better to not touch this value!)
Expand Down
100 changes: 82 additions & 18 deletions tests/utils/test_string_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
Unit tests for strings module.
Created: Dmitrii Gusev, 15.04.2019
Modified: Dmitrii Gusev, 22.11.2022
Modified: Dmitrii Gusev, 25.11.2022
"""

import pytest
import unittest
from pyutilities.utils.string_utils import filter_str
import pyutilities.utils.string_utils as pystr
from hypothesis import given
from hypothesis.strategies import characters, text
from pyutilities.utils.string_utils import trim_to_none, trim_to_empty, filter_str, process_url

# common constants for testing
EMPTY_STRINGS = ["", " ", None, "", " "]
Expand All @@ -37,31 +38,22 @@ def tearDownClass(cls):
# method just for the demo purpose
pass

def test_is_str_empty_with_empty_strings(self):
for s in EMPTY_STRINGS:
self.assertTrue(pystr.is_str_empty(s), "Must be True!")

def test_is_str_empty_with_non_empty_strings(self):
for k, v in NON_EMPTY_STRINGS.items():
self.assertFalse(pystr.is_str_empty(k), "Must be False!")
self.assertFalse(pystr.is_str_empty(v), "Must be False!")

def test_trim_to_none_with_empty_strings(self):
for s in EMPTY_STRINGS:
self.assertIsNone(pystr.trim_to_none(s), "Must be NoNe!")
self.assertIsNone(trim_to_none(s), "Must be NoNe!")

def test_trim_to_none_with_non_empty_strings(self):
for k, v in NON_EMPTY_STRINGS.items():
self.assertEqual(k, pystr.trim_to_none(v), "Must be equals!")
self.assertEqual(k, trim_to_none(v), "Must be equals!")

def test_trim_to_empty_with_empty_strings(self):
for s in EMPTY_STRINGS:
self.assertEqual("", pystr.trim_to_empty(s), "Must be an empty string!")
self.assertEqual("", pystr.trim_to_empty(s), "Must be an empty string!")
self.assertEqual("", trim_to_empty(s), "Must be an empty string!")
self.assertEqual("", trim_to_empty(s), "Must be an empty string!")

def test_trim_to_empty_with_non_empty_strings(self):
for k, v in NON_EMPTY_STRINGS.items():
self.assertEqual(k, pystr.trim_to_empty(v), "Must be equals!")
self.assertEqual(k, trim_to_empty(v), "Must be equals!")

def test_filter_str_for_empty(self):
for string in ["", " ", None]:
Expand Down Expand Up @@ -105,4 +97,76 @@ def test_filter_str_for_string(self):
],
)
def test_process_url(url, postfix, format_params, expected):
assert pystr.process_url(url, postfix, format_params) == expected
assert process_url(url, postfix, format_params) == expected


# todo: https://hypothesis.readthedocs.io/en/latest/data.html#hypothesis.strategies.text
# todo: https://en.wikipedia.org/wiki/Unicode_character_property
@given(
text(
alphabet=characters(
blacklist_categories=(
"Cc",
"Zs",
"Zl",
"Zp",
)
),
min_size=1,
max_size=100,
)
)
def test_trim_to_none_with_meaningful_symbols(text):
assert trim_to_none(text) == text


@given(
text(
alphabet=characters(
whitelist_categories=(
"Zs",
"Zl",
"Zp",
)
),
min_size=1,
max_size=100,
)
)
def test_trim_to_none_with_only_non_meaningful_symbols(text):
assert trim_to_none(text) is None


@given(
text(
alphabet=characters(
blacklist_categories=(
"Cc",
"Zs",
"Zl",
"Zp",
)
),
min_size=1,
max_size=100,
)
)
def test_trim_to_empty_with_meaningful_symbols(text):
assert trim_to_empty(text) == text


@given(
text(
alphabet=characters(
whitelist_categories=(
"Zs",
"Zl",
"Zp",
)
),
min_size=1,
max_size=100,
)
)
def test_trim_to_empty_with_only_non_meaningful_symbols(text):
assert trim_to_empty(text) == ""

0 comments on commit 582dacf

Please sign in to comment.