Skip to content

list of shennanigans (syntax footguns, unhelpful defaults, missing basic functionality, silently data dropping behavior) #164

@matu3ba

Description

@matu3ba
# SHENNANIGAN do not use xml.dom.minidom, it breaks space and newlines:
# https://bugs.python.org/issue5752
# use instead ElementTree

# SHENNANIGAN os.kill() does not call registered cleanup function `atexit.register(exit_cleanup)`
# by deamonzed threads. Must store pids of child processes and clean them explicitly or
# signal main thread via
def signalMainThread(self) -> None:
    pass
    # before Python 3.10: _thread.interrupt_main()
    # since Python 3.10: _thread.interrupt_main(signum=signal.SIGKILL)

# SHENNANIGAN
# readline() with timeout io file api is broken, see https://github.com/python/cpython/issues/51571
# Workaround
# * 1. Read from Kernel structure and append chunk-wise to buffer from socket until stop event.
# * 2. After each read, try line = readline() from the buffer and remove the line on success.
# * 3. On failure of reading line, continue with 1.
# * 4. Teardown should also use readSocket to read all Kernel memory, if a stop was signaled.
# Note: utf-8 decoding must also be done and select or an equivalent should be used to check,
# if data for reading exists.

# SHENNANIGAN Dictionary is missing this common method
def is_subdict(small: dict, big: dict) -> bool:
    """
    Test, if 'small' is subdict of 'big'
    Example: big = {'pl' : 'key1': {'key2': 'value2'}}
    Then small = {'pl' : 'key1': {'key2': 'value2'}, 'otherkey'..} matches,
    but small = {'pl' : 'key1': {'key2': 'value2', 'otherkey'..}}
    or small = {'pl' : 'key1': {'key2': {'value2', 'otherkey'..}}} not.
    """
    # since python3.9:
    # return big | small == big
    # also:
    # return {**big, **small} == big
    return dict(big, **small) == big

# SHENNANIGAN Dictionary is missing this common method
def has_fieldsvals(small: dict, big: dict) -> bool:
    """
    Test, if 'small' has all values of of 'big'
    Example: big = {'pl' : 'key1': {'key2': 'value2'}}
    Then small = {'pl' : 'key1': {'key2': 'value2'}, 'otherkey'..} matches,
    small = {'pl' : 'key1': {'key2': 'value2', 'otherkey'..}} matches,
    and small = {'pl' : 'key1': {'key2': {'value2', 'otherkey'..}}} matches.
    """
    for key, value in small.items():
        if key in big:
            if isinstance(small[key], dict):
                if not has_fieldsvals(small[key], big[key]):
                    return False
            elif value != big[key]:
                return False
        else:
            return False
    return True

# SHENNANIGAN Dictionary is missing this common method
def merge_dicts(alpha: dict = {}, beta: dict = {}) -> dict:
    """
    Recursive merge dicts. Not multi-threading safe.
    """
    return _merge_dicts_aux(alpha, beta, copy.copy(alpha))
def _merge_dicts_aux(alpha: dict = {}, beta: dict = {}, result: dict = {}, path: Optional[List[str]] = None) -> dict:
    if path is None:
        path = []
    for key in beta:
        if key not in alpha:
            result[key] = beta[key]
        else:
            if isinstance(alpha[key], dict) and isinstance(beta[key], dict):
                # key value is dict in A and B => merge the dicts
                _merge_dicts_aux(alpha[key], beta[key], result[key], path + [str(key)])
            elif alpha[key] == beta[key]:
                # key value is same in A and B => ignore
                pass
            else:
                # key value differs in A and B => raise error
                err: str = f"Conflict at {'.'.join(path + [str(key)])}"
                raise Exception(err)
    return result

### SHENNANIGAN tuples and dicts are annoying to differentiate
# dictionary
dict1 = {
    "m1": "cp",
    "m2": "cp"
}
# tuple
tup1 = {
    "m1": "cp",
    "m2": "cp"
},

# at least getting the intention correct, but python is still unhelpful with errror message
dict2 = dict({
    "m1": "cp",
    "m2": "cp"
})
# tuple
tup2 = tuple({
    "m1": "cp",
    "m2": "cp"
}),

Steal it from github.com/matu3ba/dotfiles/templates/common.py, if you want.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions