A Python utility belt containing simple tools, a stdlib like feel, and extra batteries. Hashing, Caching, Timing, Progress, and more made easy!

Overview

ReadTheDocs Pypi Downloads Codecov CircleCI Travis Appveyor CodeQuality

https://i.imgur.com/PoYIsWE.png

Ubelt is a small library of robust, tested, documented, and simple functions that extend the Python standard library. It has a flat API that all behaves similarly on Windows, Mac, and Linux (up to some small unavoidable differences). Almost every function in ubelt was written with a doctest. This provides helpful documentation and example usage as well as helping achieve 100% test coverage (with minor exceptions for Python2, Windows, etc...).

  • Goal: provide simple functions that accomplish common tasks not yet addressed by the python standard library.
  • Constraints: Must be low-impact pure python; it should be easy to install and use.
  • Method: All functions are written with docstrings and doctests to ensure that a baseline level of documentation and testing always exists (even if functions are copy/pasted into other libraries)
  • Motto: Good utilities lift all codes.

Read the docs here: http://ubelt.readthedocs.io/en/latest/

These are some of the tasks that ubelt's API enables:

  • hash common data structures like list, dict, int, str, etc. (hash_data)
  • hash files (hash_file)
  • cache a block of code (Cacher, CacheStamp)
  • time a block of code (Timerit, Timer)
  • show loop progress (ProgIter)
  • download a file with optional caching and hash verification (download, grabdata)
  • run shell commands (cmd)
  • find a file or directory in candidate locations (find_path, find_exe)
  • string-format nested data structures (repr2)
  • color text with ANSI tags (color_text)
  • horizontally concatenate multiline strings (hzcat)
  • make a directory if it doesn't exist (ensuredir)
  • delete a file, link, or entire directory (delete)
  • create cross platform symlinks (symlink)
  • expand environment variables and tildes in path strings (expandpath)
  • import a module using the path to that module (import_module_from_path)
  • check if a particular flag or value is on the command line (argflag, argval)
  • get paths to cross platform data/cache/config directories (ensure_app_cache_dir, ...)
  • memoize functions (memoize, memoize_method, memoize_property)
  • build ordered sets (oset)
  • short defaultdict and OrderedDict aliases (ddict and odict)
  • map a function over the keys or values of a dictionary (map_keys, map_vals)
  • perform set operations on dictionaries (dict_union, dict_isect, dict_diff, dict_subset, ...)
  • perform dictionary operations like histogram, inversion, and sorting (dict_hist, invert_dict, sorted_keys, sorted_vals)
  • argmax/min/sort on lists and dictionaries (argmin, argsort,)
  • find duplicates in a list (find_duplicates)
  • group a sequence of items by some criterion (group_items)

Ubelt is small. Its top-level API is defined using roughly 40 lines:

from ubelt.util_arg import (argflag, argval,)
from ubelt.util_cache import (CacheStamp, Cacher,)
from ubelt.util_colors import (color_text, highlight_code,)
from ubelt.util_const import (NoParam,)
from ubelt.util_cmd import (cmd,)
from ubelt.util_dict import (AutoDict, AutoOrderedDict, ddict, dict_diff,
                             dict_hist, dict_isect, dict_subset, dict_union,
                             dzip, find_duplicates, group_items, invert_dict,
                             map_keys, map_vals, odict, sorted_keys,
                             sorted_vals,)
from ubelt.util_download import (download, grabdata,)
from ubelt.util_func import (identity, inject_method,)
from ubelt.util_format import (FormatterExtensions, repr2,)
from ubelt.util_io import (delete, readfrom, touch, writeto,)
from ubelt.util_links import (symlink,)
from ubelt.util_list import (allsame, argmax, argmin, argsort, argunique,
                             boolmask, chunks, compress, flatten, iter_window,
                             iterable, peek, take, unique, unique_flags,)
from ubelt.util_hash import (hash_data, hash_file,)
from ubelt.util_import import (import_module_from_name,
                               import_module_from_path, modname_to_modpath,
                               modpath_to_modname, split_modpath,)
from ubelt.util_memoize import (memoize, memoize_method, memoize_property,)
from ubelt.util_mixins import (NiceRepr,)
from ubelt.util_path import (TempDir, augpath, ensuredir, expandpath,
                             shrinkuser, userhome,)
from ubelt.util_platform import (DARWIN, LINUX, POSIX, WIN32,
                                 ensure_app_cache_dir, ensure_app_config_dir,
                                 ensure_app_data_dir, find_exe, find_path,
                                 get_app_cache_dir, get_app_config_dir,
                                 get_app_data_dir, platform_cache_dir,
                                 platform_config_dir, platform_data_dir,)
from ubelt.util_str import (codeblock, ensure_unicode, hzcat, indent,
                            paragraph,)
from ubelt.util_stream import (CaptureStdout, CaptureStream, TeeStringIO,)
from ubelt.util_time import (timestamp,)
from ubelt.orderedset import (OrderedSet, oset,)
from ubelt.progiter import (ProgIter,)
from ubelt.timerit import (Timer, Timerit,)

Installation:

Ubelt is distributed on pypi as a universal wheel and can be pip installed on Python 2.7, Python 3.4+. Installations are tested on CPython and PyPy implementations.

pip install ubelt

Note that our distributions on pypi are signed with GPG. The signing public key is D297D757; this should agree with the value in dev/public_gpg_key.

It is also possible to simply install it from source.

pip install git+https://github.com/Erotemic/ubelt.git

History:

Ubelt is a migration of the most useful parts of utool(https://github.com/Erotemic/utool) into a standalone module with minimal dependencies.

The utool library contains a number of useful utility functions, but it also contained non-useful functions, as well as the kitchen sink. A number of the functions were too specific or not well documented. The ubelt is a port of the simplest and most useful parts of utool.

Note that there are other cool things in utool that are not in ubelt. Notably, the doctest harness ultimately became xdoctest. Code introspection and dynamic analysis tools were ported to xinspect. The more IPython-y tools were ported to xdev. Parts of it made their way into scriptconfig. The init-file generation was moved to mkinit. Some vim and system-y things can be found in vimtk.

Function Usefulness

When I had to hand pick a set of functions that I thought were the most useful I chose these and provided some comment on why:

import ubelt as ub

ub.ensuredir  # os.makedirs(exist_ok=True) is 3 only and too verbose
ub.Timerit  # powerful multiline alternative to timeit
ub.Cacher  # configuration based on-disk cachine
ub.cmd  # combines the best of subprocess.Popen and os.system
ub.hash_data  # extremely useful with Cacher to config strings
ub.repr2  # readable representations of nested data structures
ub.download  # why is this not a one liner --- also see grabdata for the same thing, but builtin caching.
ub.AutoDict  # one of the most useful tools in Perl,
ub.modname_to_modpath  # (works via static analysis)
ub.modpath_to_modname  # (works via static analysis)
ub.import_module_from_path  # (Unlike importlib, this does not break pytest)
ub.import_module_from_name  # (Unlike importlib, this does not break pytest)

But a better way might to objectively measure the frequency of usage and built a histogram of usefulness. I generated this histogram using python dev/count_usage_freq.py.

{
'repr2': 1598,
'ProgIter': 610,
'expandpath': 610,
'ensuredir': 482,
'take': 337,
'odict': 311,
'map_vals': 272,
'dzip': 246,
'augpath': 209,
'NiceRepr': 197,
'ddict': 191,
'argval': 184,
'cmd': 176,
'argflag': 171,
'flatten': 168,
'codeblock': 159,
'Timerit': 158,
'NoParam': 149,
'dict_hist': 146,
'group_items': 138,
'peek': 134,
'iterable': 124,
'hash_data': 116,
'grabdata': 93,
'delete': 82,
'compress': 76,
'color_text': 76,
'dict_subset': 72,
'Cacher': 68,
'allsame': 66,
'Timer': 57,
'argsort': 53,
'oset': 51,
'invert_dict': 50,
'indent': 47,
'chunks': 45,
'memoize': 44,
'dict_isect': 42,
'timestamp': 40,
'import_module_from_path': 39,
'unique': 36,
'map_keys': 35,
'hzcat': 35,
'find_duplicates': 35,
'writeto': 35,
'dict_union': 34,
'ensure_unicode': 30,
'readfrom': 30,
'iter_window': 29,
'sorted_vals': 29,
'argmax': 26,
'memoize_property': 26,
'modname_to_modpath': 25,
'symlink': 25,
'memoize_method': 23,
'dict_diff': 23,
'identity': 22,
'hash_file': 21,
'touch': 19,
'import_module_from_name': 17,
'highlight_code': 16,
'find_exe': 15,
'CacheStamp': 13,
'find_path': 9,
'AutoDict': 8,
'split_modpath': 7,
'shrinkuser': 7,
'argmin': 6,
'inject_method': 6,
'download': 5,
'modpath_to_modname': 5,
'paragraph': 5,
'CaptureStdout': 4,
'sorted_keys': 3,
'userhome': 2,
'AutoOrderedDict': 2,
'argunique': 2,
'unique_flags': 2,
}

Examples

Be sure to checkout the new Jupyter notebook: https://github.com/Erotemic/ubelt/blob/master/docs/notebooks/Ubelt%20Demo.ipynb

Here are some examples of some features inside ubelt

Timing

Quickly time a single line.

>>> import math
>>> import ubelt as ub
>>> timer = ub.Timer('Timer demo!', verbose=1)
>>> with timer:
>>>     math.factorial(100000)
tic('Timer demo!')
...toc('Timer demo!')=0.1453s

Robust Timing and Benchmarking

Easily do robust timings on existing blocks of code by simply indenting them. There is no need to refactor into a string representation or convert to a single line. With ub.Timerit there is no need to resort to the timeit module!

The quick and dirty way just requires one indent.

Note: Timerit is also defined in a standalone module: pip install timerit)

>>> import math
>>> import ubelt as ub
>>> for _ in ub.Timerit(num=200, verbose=3):
>>>     math.factorial(10000)
Timing for 200 loops
Timed for: 200 loops, best of 3
    time per loop: best=2.055 ms, mean=2.145 ± 0.083 ms

Use the loop variable as a context manager for more accurate timings or to incorporate an setup phase that is not timed. You can also access properties of the ub.Timerit class to programmatically use results.

>>> import math
>>> import ubelt as ub
>>> t1 = ub.Timerit(num=200, verbose=2)
>>> for timer in t1:
>>>     setup_vars = 10000
>>>     with timer:
>>>         math.factorial(setup_vars)
>>> print('t1.total_time = %r' % (t1.total_time,))
Timing for 200 loops
Timed for: 200 loops, best of 3
    time per loop: best=2.064 ms, mean=2.115 ± 0.05 ms
t1.total_time = 0.4427177629695507

Loop Progress

ProgIter is a no-threads attached Progress meter that writes to stdout. It is a mostly drop-in alternative to tqdm. The advantage of ``ProgIter`` is that it does not use any python threading, and therefore can be safer with code that makes heavy use of multiprocessing.

Note: ProgIter is also defined in a standalone module: pip install progiter)

>>> import ubelt as ub
>>> def is_prime(n):
...     return n >= 2 and not any(n % i == 0 for i in range(2, n))
>>> for n in ub.ProgIter(range(1000), verbose=2):
>>>     # do some work
>>>     is_prime(n)
    0/1000... rate=0.00 Hz, eta=?, total=0:00:00, wall=14:05 EST
    1/1000... rate=82241.25 Hz, eta=0:00:00, total=0:00:00, wall=14:05 EST
  257/1000... rate=177204.69 Hz, eta=0:00:00, total=0:00:00, wall=14:05 EST
  642/1000... rate=94099.22 Hz, eta=0:00:00, total=0:00:00, wall=14:05 EST
 1000/1000... rate=71886.74 Hz, eta=0:00:00, total=0:00:00, wall=14:05 EST

Caching

Cache intermediate results in a script with minimal boilerplate. It looks like 4 lines of boilerplate is the best you can do with Python 3.8 syntax. See <https://raw.githubusercontent.com/Erotemic/ubelt/master/ubelt/util_cache.py>`__ for details.

>>> import ubelt as ub
>>> cfgstr = 'repr-of-params-that-uniquely-determine-the-process'
>>> cacher = ub.Cacher('test_process', cfgstr)
>>> data = cacher.tryload()
>>> if data is None:
>>>     myvar1 = 'result of expensive process'
>>>     myvar2 = 'another result'
>>>     data = myvar1, myvar2
>>>     cacher.save(data)
>>> myvar1, myvar2 = data

Hashing

The ub.hash_data constructs a hash corresponding to a (mostly) arbitrary ordered python object. A common use case for this function is to construct the cfgstr mentioned in the example for ub.Cacher. Instead of returning a hex, string, ub.hash_data encodes the hash digest using the 26 lowercase letters in the roman alphabet. This makes the result easy to use as a filename suffix.

>>> import ubelt as ub
>>> data = [('arg1', 5), ('lr', .01), ('augmenters', ['flip', 'translate'])]
>>> ub.hash_data(data)[0:8]
5f5fda5e

There exists an undocumented plugin architecture to extend this function to arbitrary types. See ubelt/util_hash.py for details.

Command Line Interaction

The builtin Python subprocess.Popen module is great, but it can be a bit clunky at times. The os.system command is easy to use, but it doesn't have much flexibility. The ub.cmd function aims to fix this. It is as simple to run as os.system, but it returns a dictionary containing the return code, standard out, standard error, and the Popen object used under the hood.

>>> import ubelt as ub
>>> info = ub.cmd('gcc --version')
>>> print(ub.repr2(info))
{
    'command': 'gcc --version',
    'err': '',
    'out': 'gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609\nCopyright (C) 2015 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n',
    'proc': <subprocess.Popen object at 0x7ff98b310390>,
    'ret': 0,
}

Also note the use of ub.repr2 to nicely format the output dictionary.

Additionally, if you specify verbose=True, ub.cmd will simultaneously capture the standard output and display it in real time.

>>> import ubelt as ub
>>> info = ub.cmd('gcc --version', verbose=True)
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

A common use case for ub.cmd is parsing version numbers of programs

>>> import ubelt as ub
>>> cmake_version = ub.cmd('cmake --version')['out'].splitlines()[0].split()[-1]
>>> print('cmake_version = {!r}'.format(cmake_version))
cmake_version = 3.11.0-rc2

This allows you to easily run a command line executable as part of a python process, see what it is doing, and then do something based on its output, just as you would if you were interacting with the command line itself.

Lastly, ub.cmd removes the need to think about if you need to pass a list of args, or a string. Both will work. This utility has been tested on both Windows and Linux.

Cross-Platform Resource and Cache Directories

If you have an application which writes configuration or cache files, the standard place to dump those files differs depending if you are on Windows, Linux, or Mac. Ubelt offers a unified functions for determining what these paths are.

The ub.ensure_app_cache_dir and ub.ensure_app_resource_dir functions find the correct platform-specific location for these files and ensures that the directories exist. (Note: replacing "ensure" with "get" will simply return the path, but not ensure that it exists)

The resource root directory is ~/AppData/Roaming on Windows, ~/.config on Linux and ~/Library/Application Support on Mac. The cache root directory is ~/AppData/Local on Windows, ~/.config on Linux and ~/Library/Caches on Mac.

Example usage on Linux might look like this:

>>> import ubelt as ub
>>> print(ub.compressuser(ub.ensure_app_cache_dir('my_app')))
~/.cache/my_app
>>> print(ub.compressuser(ub.ensure_app_resource_dir('my_app')))
~/.config/my_app

Symlinks

The ub.symlink function will create a symlink similar to os.symlink. The main differences are that 1) it will not error if the symlink exists and already points to the correct location. 2) it works* on Windows (*hard links and junctions are used if real symlinks are not available)

>>> import ubelt as ub
>>> dpath = ub.ensure_app_cache_dir('ubelt', 'demo_symlink')
>>> real_path = join(dpath, 'real_file.txt')
>>> link_path = join(dpath, 'link_file.txt')
>>> ub.writeto(real_path, 'foo')
>>> ub.symlink(real_path, link_path)

Downloading Files

The function ub.download provides a simple interface to download a URL and save its data to a file.

>>> import ubelt as ub
>>> url = 'http://i.imgur.com/rqwaDag.png'
>>> fpath = ub.download(url, verbose=0)
>>> print(ub.compressuser(fpath))
~/.cache/ubelt/rqwaDag.png

The function ub.grabdata works similarly to ub.download, but whereas ub.download will always re-download the file, ub.grabdata will check if the file exists and only re-download it if it needs to.

>>> import ubelt as ub
>>> url = 'http://i.imgur.com/rqwaDag.png'
>>> fpath = ub.grabdata(url, verbose=0, hash_prefix='944389a39')
>>> print(ub.compressuser(fpath))
~/.cache/ubelt/rqwaDag.png

New in version 0.4.0: both functions now accepts the hash_prefix keyword argument, which if specified will check that the hash of the file matches the provided value. The hasher keyword argument can be used to change which hashing algorithm is used (it defaults to "sha512").

Grouping

Group items in a sequence into a dictionary by a second id list

>>> import ubelt as ub
>>> item_list    = ['ham',     'jam',   'spam',     'eggs',    'cheese', 'bannana']
>>> groupid_list = ['protein', 'fruit', 'protein',  'protein', 'dairy',  'fruit']
>>> ub.group_items(item_list, groupid_list)
{'dairy': ['cheese'], 'fruit': ['jam', 'bannana'], 'protein': ['ham', 'spam', 'eggs']}

Dictionary Histogram

Find the frequency of items in a sequence

>>> import ubelt as ub
>>> item_list = [1, 2, 39, 900, 1232, 900, 1232, 2, 2, 2, 900]
>>> ub.dict_hist(item_list)
{1232: 2, 1: 1, 2: 4, 900: 3, 39: 1}

Find Duplicates

Find all duplicate items in a list. More specifically, ub.find_duplicates searches for items that appear more than k times, and returns a mapping from each duplicate item to the positions it appeared in.

>>> import ubelt as ub
>>> items = [0, 0, 1, 2, 3, 3, 0, 12, 2, 9]
>>> ub.find_duplicates(items, k=2)
{0: [0, 1, 6], 2: [3, 8], 3: [4, 5]}

Dictionary Manipulation

Take a subset of a dictionary.

>>> import ubelt as ub
>>> dict_ = {'K': 3, 'dcvs_clip_max': 0.2, 'p': 0.1}
>>> subdict_ = ub.dict_subset(dict_, ['K', 'dcvs_clip_max'])
>>> print(subdict_)
{'K': 3, 'dcvs_clip_max': 0.2}

Take only the values, optionally specify a default value.

>>> import ubelt as ub
>>> dict_ = {1: 'a', 2: 'b', 3: 'c'}
>>> print(list(ub.take(dict_, [1, 2, 3, 4, 5], default=None)))
['a', 'b', 'c', None, None]

Apply a function to each value in the dictionary (see also ub.map_keys).

>>> import ubelt as ub
>>> dict_ = {'a': [1, 2, 3], 'b': []}
>>> newdict = ub.map_vals(len, dict_)
>>> print(newdict)
{'a': 3, 'b': 0}

Invert the mapping defined by a dictionary. By default invert_dict assumes that all dictionary values are distinct (i.e. the mapping is one-to-one / injective).

>>> import ubelt as ub
>>> mapping = {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
>>> ub.invert_dict(mapping)
{'a': 0, 'b': 1, 'c': 2, 'd': 3}

However, by specifying unique_vals=False the inverted dictionary builds a set of keys that were associated with each value.

>>> import ubelt as ub
>>> mapping = {'a': 0, 'A': 0, 'b': 1, 'c': 2, 'C': 2, 'd': 3}
>>> ub.invert_dict(mapping, unique_vals=False)
{0: {'A', 'a'}, 1: {'b'}, 2: {'C', 'c'}, 3: {'d'}}

AutoDict - Autovivification

While the collections.defaultdict is nice, it is sometimes more convenient to have an infinitely nested dictionary of dictionaries.

>>> import ubelt as ub
>>> auto = ub.AutoDict()
>>> print('auto = {!r}'.format(auto))
auto = {}
>>> auto[0][10][100] = None
>>> print('auto = {!r}'.format(auto))
auto = {0: {10: {100: None}}}
>>> auto[0][1] = 'hello'
>>> print('auto = {!r}'.format(auto))
auto = {0: {1: 'hello', 10: {100: None}}}

String-based imports

Ubelt contains functions to import modules dynamically without using the python import statement. While importlib exists, the ubelt implementation is simpler to user and does not have the disadvantage of breaking pytest.

Note ubelt simply provides an interface to this functionality, the core implementation is in xdoctest (over as of version 0.7.0, the code is statically copied into an autogenerated file such that ubelt does not actually depend on xdoctest during runtime).

>>> import ubelt as ub
>>> module = ub.import_module_from_path(ub.expandpath('~/code/ubelt/ubelt'))
>>> print('module = {!r}'.format(module))
module = <module 'ubelt' from '/home/joncrall/code/ubelt/ubelt/__init__.py'>
>>> module = ub.import_module_from_name('ubelt')
>>> print('module = {!r}'.format(module))
module = <module 'ubelt' from '/home/joncrall/code/ubelt/ubelt/__init__.py'>

Related to this functionality are the functions ub.modpath_to_modname and ub.modname_to_modpath, which statically transform (i.e. no code in the target modules is imported or executed) between module names (e.g. ubelt.util_import) and module paths (e.g. ~/.local/conda/envs/cenv3/lib/python3.5/site-packages/ubelt/util_import.py).

>>> import ubelt as ub
>>> modpath = ub.util_import.__file__
>>> print(ub.modpath_to_modname(modpath))
ubelt.util_import
>>> modname = ub.util_import.__name__
>>> assert ub.modname_to_modpath(modname) == modpath

Horizontal String Concatenation

Sometimes its just prettier to horizontally concatenate two blocks of text.

>>> import ubelt as ub
>>> B = ub.repr2([[1, 2], [3, 4]], nl=1, cbr=True, trailsep=False)
>>> C = ub.repr2([[5, 6], [7, 8]], nl=1, cbr=True, trailsep=False)
>>> print(ub.hzcat(['A = ', B, ' * ', C]))
A = [[1, 2], * [[5, 6],
     [3, 4]]    [7, 8]]

External tools.

Some of the tools in ubelt also exist as standalone modules. I haven't decided if its best to statically copy them into ubelt or require on pypi to satisfy the dependency. There are some tools that are not used by default unless you explicitly allow for them.

Code that is currently statically included:

Code that is currently linked via pypi:

Code that is completely optional, and only used in specific cases:

  • Numpy - ub.repr2 will format a numpy array nicely by default
  • xxhash - this can be specified as a hasher to ub.hash_data
  • Pygments - used by the util_color module.

Also, in the future some of the functionality in ubelt may be ported and integrated into the boltons project: https://github.com/mahmoud/boltons.

Notes.

Ubelt will support Python2 for the foreseeable future (at least until the projects I work on are off it followed by a probation period).

PRs are welcome. If you have a utility function that you think is useful then write a PR. I'm likely to respond promptly.

Also check out my other projects (many of which are powered by ubelt):

Comments
  • pathlib is missing in requirements/tests.txt

    pathlib is missing in requirements/tests.txt

    Describe the bug Some tests expect pathlib:

    tests/test_path.py::test_pathlib SKIPPED (pathlib is not installed) [ 83%]

    but it isn't in requirements/tests.txt

    opened by yurivict 5
  • 23 errors in tests

    23 errors in tests

    Describe the bug

    =========================================================================================== ERRORS ===========================================================================================
    __________________________________________________________________________ ERROR at setup of test_download_no_fpath __________________________________________________________________________
    
    module = <module 'test_download' from '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.1/tests/test_download.py'>
    
        def setup_module(module):
            """ setup any state specific to the execution of the given module."""
    >       SingletonTestServer.instance()
    
    tests/test_download.py:664: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    tests/test_download.py:419: in instance
        self = cls()
    tests/test_download.py:463: in __init__
        info = ub.cmd(server_cmd, detach=True, cwd=dpath)
    ubelt/util_cmd.py:290: in cmd
        info = {'proc': make_proc(), 'command': command_text}
    ubelt/util_cmd.py:276: in make_proc
        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
    /usr/local/lib/python3.9/subprocess.py:951: in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <Popen: returncode: 255 args: ['python', '-m', 'http.server', '43123']>, args = ['python', '-m', 'http.server', '43123'], executable = b'python', preexec_fn = None, close_fds = True
    pass_fds = (), cwd = '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/.cache/ubelt/tests/test_download/simple_server', env = None, startupinfo = None, creationflags = 0, shell = False
    p2cread = -1, p2cwrite = -1, c2pread = 11, c2pwrite = 12, errread = 13, errwrite = 14, restore_signals = True, gid = None, gids = None, uid = None, umask = -1, start_new_session = False
    
        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           pass_fds, cwd, env,
                           startupinfo, creationflags, shell,
                           p2cread, p2cwrite,
                           c2pread, c2pwrite,
                           errread, errwrite,
                           restore_signals,
                           gid, gids, uid, umask,
                           start_new_session):
            """Execute program (POSIX version)"""
        
            if isinstance(args, (str, bytes)):
                args = [args]
            elif isinstance(args, os.PathLike):
                if shell:
                    raise TypeError('path-like args is not allowed when '
                                    'shell is true')
                args = [args]
            else:
                args = list(args)
        
            if shell:
                # On Android the default shell is at '/system/bin/sh'.
                unix_shell = ('/system/bin/sh' if
                          hasattr(sys, 'getandroidapilevel') else '/bin/sh')
                args = [unix_shell, "-c"] + args
                if executable:
                    args[0] = executable
        
            if executable is None:
                executable = args[0]
        
            sys.audit("subprocess.Popen", executable, args, cwd, env)
        
            if (_USE_POSIX_SPAWN
                    and os.path.dirname(executable)
                    and preexec_fn is None
                    and not close_fds
                    and not pass_fds
                    and cwd is None
                    and (p2cread == -1 or p2cread > 2)
                    and (c2pwrite == -1 or c2pwrite > 2)
                    and (errwrite == -1 or errwrite > 2)
                    and not start_new_session
                    and gid is None
                    and gids is None
                    and uid is None
                    and umask < 0):
                self._posix_spawn(args, executable, env, restore_signals,
                                  p2cread, p2cwrite,
                                  c2pread, c2pwrite,
                                  errread, errwrite)
                return
        
            orig_executable = executable
        
            # For transferring possible exec failure from child to parent.
            # Data format: "exception name:hex errno:description"
            # Pickle is not used; it is complex and involves memory allocation.
            errpipe_read, errpipe_write = os.pipe()
            # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
            low_fds_to_close = []
            while errpipe_write < 3:
                low_fds_to_close.append(errpipe_write)
                errpipe_write = os.dup(errpipe_write)
            for low_fd in low_fds_to_close:
                os.close(low_fd)
            try:
                try:
                    # We must avoid complex work that could involve
                    # malloc or free in the child process to avoid
                    # potential deadlocks, thus we do all this here.
                    # and pass it to fork_exec()
        
                    if env is not None:
                        env_list = []
                        for k, v in env.items():
                            k = os.fsencode(k)
                            if b'=' in k:
                                raise ValueError("illegal environment variable name")
                            env_list.append(k + b'=' + os.fsencode(v))
                    else:
                        env_list = None  # Use execv instead of execve.
                    executable = os.fsencode(executable)
                    if os.path.dirname(executable):
                        executable_list = (executable,)
                    else:
                        # This matches the behavior of os._execvpe().
                        executable_list = tuple(
                            os.path.join(os.fsencode(dir), executable)
                            for dir in os.get_exec_path(env))
                    fds_to_keep = set(pass_fds)
                    fds_to_keep.add(errpipe_write)
                    self.pid = _posixsubprocess.fork_exec(
                            args, executable_list,
                            close_fds, tuple(sorted(map(int, fds_to_keep))),
                            cwd, env_list,
                            p2cread, p2cwrite, c2pread, c2pwrite,
                            errread, errwrite,
                            errpipe_read, errpipe_write,
                            restore_signals, start_new_session,
                            gid, gids, uid, umask,
                            preexec_fn)
                    self._child_created = True
                finally:
                    # be sure the FD is closed no matter what
                    os.close(errpipe_write)
        
                self._close_pipe_fds(p2cread, p2cwrite,
                                     c2pread, c2pwrite,
                                     errread, errwrite)
        
                # Wait for exec to fail or succeed; possibly raising an
                # exception (limited in size)
                errpipe_data = bytearray()
                while True:
                    part = os.read(errpipe_read, 50000)
                    errpipe_data += part
                    if not part or len(errpipe_data) > 50000:
                        break
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_read)
        
            if errpipe_data:
                try:
                    pid, sts = os.waitpid(self.pid, 0)
                    if pid == self.pid:
                        self._handle_exitstatus(sts)
                    else:
                        self.returncode = sys.maxsize
                except ChildProcessError:
                    pass
        
                try:
                    exception_name, hex_errno, err_msg = (
                            errpipe_data.split(b':', 2))
                    # The encoding here should match the encoding
                    # written in by the subprocess implementations
                    # like _posixsubprocess
                    err_msg = err_msg.decode()
                except ValueError:
                    exception_name = b'SubprocessError'
                    hex_errno = b'0'
                    err_msg = 'Bad exception data from child: {!r}'.format(
                                  bytes(errpipe_data))
                child_exception_type = getattr(
                        builtins, exception_name.decode('ascii'),
                        SubprocessError)
                if issubclass(child_exception_type, OSError) and hex_errno:
                    errno_num = int(hex_errno, 16)
                    child_exec_never_called = (err_msg == "noexec")
                    if child_exec_never_called:
                        err_msg = ""
                        # The error must be from chdir(cwd).
                        err_filename = cwd
                    else:
                        err_filename = orig_executable
                    if errno_num != 0:
                        err_msg = os.strerror(errno_num)
    >               raise child_exception_type(errno_num, err_msg, err_filename)
    E               FileNotFoundError: [Errno 2] No such file or directory: 'python'
    
    /usr/local/lib/python3.9/subprocess.py:1821: FileNotFoundError
    ----------------------------------------------------------------------------------- Captured stdout setup ------------------------------------------------------------------------------------
    port = 43123
    _________________________________________________________________________ ERROR at setup of test_download_with_fpath _________________________________________________________________________
    
    module = <module 'test_download' from '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.1/tests/test_download.py'>
    
        def setup_module(module):
            """ setup any state specific to the execution of the given module."""
    >       SingletonTestServer.instance()
    
    tests/test_download.py:664: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    tests/test_download.py:419: in instance
        self = cls()
    tests/test_download.py:463: in __init__
        info = ub.cmd(server_cmd, detach=True, cwd=dpath)
    ubelt/util_cmd.py:290: in cmd
        info = {'proc': make_proc(), 'command': command_text}
    ubelt/util_cmd.py:276: in make_proc
        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
    /usr/local/lib/python3.9/subprocess.py:951: in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <Popen: returncode: 255 args: ['python', '-m', 'http.server', '43123']>, args = ['python', '-m', 'http.server', '43123'], executable = b'python', preexec_fn = None, close_fds = True
    pass_fds = (), cwd = '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/.cache/ubelt/tests/test_download/simple_server', env = None, startupinfo = None, creationflags = 0, shell = False
    p2cread = -1, p2cwrite = -1, c2pread = 11, c2pwrite = 12, errread = 13, errwrite = 14, restore_signals = True, gid = None, gids = None, uid = None, umask = -1, start_new_session = False
    
        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           pass_fds, cwd, env,
                           startupinfo, creationflags, shell,
                           p2cread, p2cwrite,
                           c2pread, c2pwrite,
                           errread, errwrite,
                           restore_signals,
                           gid, gids, uid, umask,
                           start_new_session):
            """Execute program (POSIX version)"""
        
            if isinstance(args, (str, bytes)):
                args = [args]
            elif isinstance(args, os.PathLike):
                if shell:
                    raise TypeError('path-like args is not allowed when '
                                    'shell is true')
                args = [args]
            else:
                args = list(args)
        
            if shell:
                # On Android the default shell is at '/system/bin/sh'.
                unix_shell = ('/system/bin/sh' if
                          hasattr(sys, 'getandroidapilevel') else '/bin/sh')
                args = [unix_shell, "-c"] + args
                if executable:
                    args[0] = executable
        
            if executable is None:
                executable = args[0]
        
            sys.audit("subprocess.Popen", executable, args, cwd, env)
        
            if (_USE_POSIX_SPAWN
                    and os.path.dirname(executable)
                    and preexec_fn is None
                    and not close_fds
                    and not pass_fds
                    and cwd is None
                    and (p2cread == -1 or p2cread > 2)
                    and (c2pwrite == -1 or c2pwrite > 2)
                    and (errwrite == -1 or errwrite > 2)
                    and not start_new_session
                    and gid is None
                    and gids is None
                    and uid is None
                    and umask < 0):
                self._posix_spawn(args, executable, env, restore_signals,
                                  p2cread, p2cwrite,
                                  c2pread, c2pwrite,
                                  errread, errwrite)
                return
        
            orig_executable = executable
        
            # For transferring possible exec failure from child to parent.
            # Data format: "exception name:hex errno:description"
            # Pickle is not used; it is complex and involves memory allocation.
            errpipe_read, errpipe_write = os.pipe()
            # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
            low_fds_to_close = []
            while errpipe_write < 3:
                low_fds_to_close.append(errpipe_write)
                errpipe_write = os.dup(errpipe_write)
            for low_fd in low_fds_to_close:
                os.close(low_fd)
            try:
                try:
                    # We must avoid complex work that could involve
                    # malloc or free in the child process to avoid
                    # potential deadlocks, thus we do all this here.
                    # and pass it to fork_exec()
        
                    if env is not None:
                        env_list = []
                        for k, v in env.items():
                            k = os.fsencode(k)
                            if b'=' in k:
                                raise ValueError("illegal environment variable name")
                            env_list.append(k + b'=' + os.fsencode(v))
                    else:
                        env_list = None  # Use execv instead of execve.
                    executable = os.fsencode(executable)
                    if os.path.dirname(executable):
                        executable_list = (executable,)
                    else:
                        # This matches the behavior of os._execvpe().
                        executable_list = tuple(
                            os.path.join(os.fsencode(dir), executable)
                            for dir in os.get_exec_path(env))
                    fds_to_keep = set(pass_fds)
                    fds_to_keep.add(errpipe_write)
                    self.pid = _posixsubprocess.fork_exec(
                            args, executable_list,
                            close_fds, tuple(sorted(map(int, fds_to_keep))),
                            cwd, env_list,
                            p2cread, p2cwrite, c2pread, c2pwrite,
                            errread, errwrite,
                            errpipe_read, errpipe_write,
                            restore_signals, start_new_session,
                            gid, gids, uid, umask,
                            preexec_fn)
                    self._child_created = True
                finally:
                    # be sure the FD is closed no matter what
                    os.close(errpipe_write)
        
                self._close_pipe_fds(p2cread, p2cwrite,
                                     c2pread, c2pwrite,
                                     errread, errwrite)
        
                # Wait for exec to fail or succeed; possibly raising an
                # exception (limited in size)
                errpipe_data = bytearray()
                while True:
                    part = os.read(errpipe_read, 50000)
                    errpipe_data += part
                    if not part or len(errpipe_data) > 50000:
                        break
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_read)
        
            if errpipe_data:
                try:
                    pid, sts = os.waitpid(self.pid, 0)
                    if pid == self.pid:
                        self._handle_exitstatus(sts)
                    else:
                        self.returncode = sys.maxsize
                except ChildProcessError:
                    pass
        
                try:
                    exception_name, hex_errno, err_msg = (
                            errpipe_data.split(b':', 2))
                    # The encoding here should match the encoding
                    # written in by the subprocess implementations
                    # like _posixsubprocess
                    err_msg = err_msg.decode()
                except ValueError:
                    exception_name = b'SubprocessError'
                    hex_errno = b'0'
                    err_msg = 'Bad exception data from child: {!r}'.format(
                                  bytes(errpipe_data))
                child_exception_type = getattr(
                        builtins, exception_name.decode('ascii'),
                        SubprocessError)
                if issubclass(child_exception_type, OSError) and hex_errno:
                    errno_num = int(hex_errno, 16)
                    child_exec_never_called = (err_msg == "noexec")
                    if child_exec_never_called:
                        err_msg = ""
                        # The error must be from chdir(cwd).
                        err_filename = cwd
                    else:
                        err_filename = orig_executable
                    if errno_num != 0:
                        err_msg = os.strerror(errno_num)
    >               raise child_exception_type(errno_num, err_msg, err_filename)
    E               FileNotFoundError: [Errno 2] No such file or directory: 'python'
    
    /usr/local/lib/python3.9/subprocess.py:1821: FileNotFoundError
    _________________________________________________________________________ ERROR at setup of test_download_chunksize __________________________________________________________________________
    
    module = <module 'test_download' from '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.1/tests/test_download.py'>
    
        def setup_module(module):
            """ setup any state specific to the execution of the given module."""
    >       SingletonTestServer.instance()
    
    tests/test_download.py:664: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    tests/test_download.py:419: in instance
        self = cls()
    tests/test_download.py:463: in __init__
        info = ub.cmd(server_cmd, detach=True, cwd=dpath)
    ubelt/util_cmd.py:290: in cmd
        info = {'proc': make_proc(), 'command': command_text}
    ubelt/util_cmd.py:276: in make_proc
        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
    /usr/local/lib/python3.9/subprocess.py:951: in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <Popen: returncode: 255 args: ['python', '-m', 'http.server', '43123']>, args = ['python', '-m', 'http.server', '43123'], executable = b'python', preexec_fn = None, close_fds = True
    pass_fds = (), cwd = '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/.cache/ubelt/tests/test_download/simple_server', env = None, startupinfo = None, creationflags = 0, shell = False
    p2cread = -1, p2cwrite = -1, c2pread = 11, c2pwrite = 12, errread = 13, errwrite = 14, restore_signals = True, gid = None, gids = None, uid = None, umask = -1, start_new_session = False
    
        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           pass_fds, cwd, env,
                           startupinfo, creationflags, shell,
                           p2cread, p2cwrite,
                           c2pread, c2pwrite,
                           errread, errwrite,
                           restore_signals,
                           gid, gids, uid, umask,
                           start_new_session):
            """Execute program (POSIX version)"""
        
            if isinstance(args, (str, bytes)):
                args = [args]
            elif isinstance(args, os.PathLike):
                if shell:
                    raise TypeError('path-like args is not allowed when '
                                    'shell is true')
                args = [args]
            else:
                args = list(args)
        
            if shell:
                # On Android the default shell is at '/system/bin/sh'.
                unix_shell = ('/system/bin/sh' if
                          hasattr(sys, 'getandroidapilevel') else '/bin/sh')
                args = [unix_shell, "-c"] + args
                if executable:
                    args[0] = executable
        
            if executable is None:
                executable = args[0]
        
            sys.audit("subprocess.Popen", executable, args, cwd, env)
        
            if (_USE_POSIX_SPAWN
                    and os.path.dirname(executable)
                    and preexec_fn is None
                    and not close_fds
                    and not pass_fds
                    and cwd is None
                    and (p2cread == -1 or p2cread > 2)
                    and (c2pwrite == -1 or c2pwrite > 2)
                    and (errwrite == -1 or errwrite > 2)
                    and not start_new_session
                    and gid is None
                    and gids is None
                    and uid is None
                    and umask < 0):
                self._posix_spawn(args, executable, env, restore_signals,
                                  p2cread, p2cwrite,
                                  c2pread, c2pwrite,
                                  errread, errwrite)
                return
        
            orig_executable = executable
        
            # For transferring possible exec failure from child to parent.
            # Data format: "exception name:hex errno:description"
            # Pickle is not used; it is complex and involves memory allocation.
            errpipe_read, errpipe_write = os.pipe()
            # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
            low_fds_to_close = []
            while errpipe_write < 3:
                low_fds_to_close.append(errpipe_write)
                errpipe_write = os.dup(errpipe_write)
            for low_fd in low_fds_to_close:
                os.close(low_fd)
            try:
                try:
                    # We must avoid complex work that could involve
                    # malloc or free in the child process to avoid
                    # potential deadlocks, thus we do all this here.
                    # and pass it to fork_exec()
        
                    if env is not None:
                        env_list = []
                        for k, v in env.items():
                            k = os.fsencode(k)
                            if b'=' in k:
                                raise ValueError("illegal environment variable name")
                            env_list.append(k + b'=' + os.fsencode(v))
                    else:
                        env_list = None  # Use execv instead of execve.
                    executable = os.fsencode(executable)
                    if os.path.dirname(executable):
                        executable_list = (executable,)
                    else:
                        # This matches the behavior of os._execvpe().
                        executable_list = tuple(
                            os.path.join(os.fsencode(dir), executable)
                            for dir in os.get_exec_path(env))
                    fds_to_keep = set(pass_fds)
                    fds_to_keep.add(errpipe_write)
                    self.pid = _posixsubprocess.fork_exec(
                            args, executable_list,
                            close_fds, tuple(sorted(map(int, fds_to_keep))),
                            cwd, env_list,
                            p2cread, p2cwrite, c2pread, c2pwrite,
                            errread, errwrite,
                            errpipe_read, errpipe_write,
                            restore_signals, start_new_session,
                            gid, gids, uid, umask,
                            preexec_fn)
                    self._child_created = True
                finally:
                    # be sure the FD is closed no matter what
                    os.close(errpipe_write)
        
                self._close_pipe_fds(p2cread, p2cwrite,
                                     c2pread, c2pwrite,
                                     errread, errwrite)
        
                # Wait for exec to fail or succeed; possibly raising an
                # exception (limited in size)
                errpipe_data = bytearray()
                while True:
                    part = os.read(errpipe_read, 50000)
                    errpipe_data += part
                    if not part or len(errpipe_data) > 50000:
                        break
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_read)
        
            if errpipe_data:
                try:
                    pid, sts = os.waitpid(self.pid, 0)
                    if pid == self.pid:
                        self._handle_exitstatus(sts)
                    else:
                        self.returncode = sys.maxsize
                except ChildProcessError:
                    pass
        
                try:
                    exception_name, hex_errno, err_msg = (
                            errpipe_data.split(b':', 2))
                    # The encoding here should match the encoding
                    # written in by the subprocess implementations
                    # like _posixsubprocess
                    err_msg = err_msg.decode()
                except ValueError:
                    exception_name = b'SubprocessError'
                    hex_errno = b'0'
                    err_msg = 'Bad exception data from child: {!r}'.format(
                                  bytes(errpipe_data))
                child_exception_type = getattr(
                        builtins, exception_name.decode('ascii'),
                        SubprocessError)
                if issubclass(child_exception_type, OSError) and hex_errno:
                    errno_num = int(hex_errno, 16)
                    child_exec_never_called = (err_msg == "noexec")
                    if child_exec_never_called:
                        err_msg = ""
                        # The error must be from chdir(cwd).
                        err_filename = cwd
                    else:
                        err_filename = orig_executable
                    if errno_num != 0:
                        err_msg = os.strerror(errno_num)
    >               raise child_exception_type(errno_num, err_msg, err_filename)
    E               FileNotFoundError: [Errno 2] No such file or directory: 'python'
    
    /usr/local/lib/python3.9/subprocess.py:1821: FileNotFoundError
    _______________________________________________________________________ ERROR at setup of test_download_cover_hashers ________________________________________________________________________
    
    module = <module 'test_download' from '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.1/tests/test_download.py'>
    
        def setup_module(module):
            """ setup any state specific to the execution of the given module."""
    >       SingletonTestServer.instance()
    
    tests/test_download.py:664: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    tests/test_download.py:419: in instance
        self = cls()
    tests/test_download.py:463: in __init__
        info = ub.cmd(server_cmd, detach=True, cwd=dpath)
    ubelt/util_cmd.py:290: in cmd
        info = {'proc': make_proc(), 'command': command_text}
    ubelt/util_cmd.py:276: in make_proc
        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
    /usr/local/lib/python3.9/subprocess.py:951: in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <Popen: returncode: 255 args: ['python', '-m', 'http.server', '43123']>, args = ['python', '-m', 'http.server', '43123'], executable = b'python', preexec_fn = None, close_fds = True
    pass_fds = (), cwd = '/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/.cache/ubelt/tests/test_download/simple_server', env = None, startupinfo = None, creationflags = 0, shell = False
    p2cread = -1, p2cwrite = -1, c2pread = 11, c2pwrite = 12, errread = 13, errwrite = 14, restore_signals = True, gid = None, gids = None, uid = None, umask = -1, start_new_session = False
    
        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           pass_fds, cwd, env,
                           startupinfo, creationflags, shell,
                           p2cread, p2cwrite,
                           c2pread, c2pwrite,
                           errread, errwrite,
                           restore_signals,
                           gid, gids, uid, umask,
                           start_new_session):
            """Execute program (POSIX version)"""
        
            if isinstance(args, (str, bytes)):
                args = [args]
            elif isinstance(args, os.PathLike):
                if shell:
                    raise TypeError('path-like args is not allowed when '
                                    'shell is true')
                args = [args]
            else:
                args = list(args)
        
            if shell:
                # On Android the default shell is at '/system/bin/sh'.
                unix_shell = ('/system/bin/sh' if
                          hasattr(sys, 'getandroidapilevel') else '/bin/sh')
                args = [unix_shell, "-c"] + args
                if executable:
                    args[0] = executable
        
            if executable is None:
                executable = args[0]
        
            sys.audit("subprocess.Popen", executable, args, cwd, env)
        
            if (_USE_POSIX_SPAWN
                    and os.path.dirname(executable)
                    and preexec_fn is None
                    and not close_fds
                    and not pass_fds
                    and cwd is None
                    and (p2cread == -1 or p2cread > 2)
                    and (c2pwrite == -1 or c2pwrite > 2)
                    and (errwrite == -1 or errwrite > 2)
                    and not start_new_session
                    and gid is None
                    and gids is None
                    and uid is None
                    and umask < 0):
                self._posix_spawn(args, executable, env, restore_signals,
                                  p2cread, p2cwrite,
                                  c2pread, c2pwrite,
                                  errread, errwrite)
                return
        
            orig_executable = executable
        
            # For transferring possible exec failure from child to parent.
            # Data format: "exception name:hex errno:description"
            # Pickle is not used; it is complex and involves memory allocation.
            errpipe_read, errpipe_write = os.pipe()
            # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
            low_fds_to_close = []
            while errpipe_write < 3:
    

    Expected behavior A clear and concise description of what you expected to happen.

    Desktop (please complete the following information):

    • OS: FreeBSD 13.1
    • Ubelt version 1.2.1
    • Python version 3.9

    Additional context Add any other context about the problem here.

    opened by yurivict 5
  • Test ubelt/util_platform.py::find_exe:0 fails if which(1) is not installed

    Test ubelt/util_platform.py::find_exe:0 fails if which(1) is not installed

    Describe the bug Contrary to the popular belief, which(1) is not part of POSIX base system and it is entirely possible not to have it installed at all. However, ubelt's tests assume that it is always present and fail when it isn't:

    ________________________________________________________ [xdoctest] find_exe:0 ________________________________________________________
    * REASON: TypeError
    DOCTEST DEBUG INFO
      XDoc "/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py::find_exe:0", line 3 <- wrt doctest
      File "/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py", line 288, <- wrt source file
    DOCTEST PART BREAKDOWN
    Failed Part:
        1 >>> find_exe('ls')
        2 >>> find_exe('ping')
        3 >>> assert find_exe('which') == find_exe(find_exe('which'))
        4 >>> find_exe('which', multi=True)
        5 >>> find_exe('ping', multi=True)
        6 >>> find_exe('cmake', multi=True)
        7 >>> find_exe('nvcc', multi=True)
        8 >>> find_exe('noexist', multi=True)
    DOCTEST TRACEBACK
    Traceback (most recent call last):
    
      File "/usr/lib/python3.8/site-packages/xdoctest/doctest_example.py", line 653, in run
        exec(code, test_globals)
    
      File "<doctest:/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py::find_exe:0>", line rel: 3, abs: 288, in <module>
        >>> assert find_exe('which') == find_exe(find_exe('which'))
    
      File "/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py", line 318, in find_exe
        for fpath in results:
    
      File "/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py", line 315, in <genexpr>
        results = (fpath for fpath in candidates
    
      File "/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py", line 386, in find_path
        for candidate in candidates:
    
      File "/tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py", line 373, in <genexpr>
        candidates = (join(dpath, name) for dpath in dpaths)
    
      File "/usr/lib/python3.8/posixpath.py", line 90, in join
        genericpath._check_arg_types('join', a, *p)
    
      File "/usr/lib/python3.8/genericpath.py", line 152, in _check_arg_types
        raise TypeError(f'{funcname}() argument must be str, bytes, or '
    
    TypeError: join() argument must be str, bytes, or os.PathLike object, not 'NoneType'
    
    DOCTEST REPRODUCTION
    CommandLine:
        pytest /tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py::find_exe:0
    /tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py:288: TypeError
    -------------------------------------------------------- Captured stdout call ---------------------------------------------------------
    ====== <exec> ======
    * DOCTEST : /tmp/portage/dev-python/ubelt-1.1.0/work/ubelt-1.1.0/ubelt/util_platform.py::find_exe:0, line 286 <- wrt source file
    

    To Reproduce

    1. Uninstall which(1).
    2. pytest ubelt/util_platform.py::find_exe

    Expected behavior Tests passing.

    Desktop (please complete the following information):

    • OS: Gentoo Linux
    • Ubelt version 1.1.0
    • Python version 3.8.13 (not really relevant)
    opened by mgorny 5
  • FileNotFoundError attempting to launch sys.executable on Windows

    FileNotFoundError attempting to launch sys.executable on Windows

    In jaraco/safety-tox, I'm attempting to write a script that will wrap tox and tee its output so the output can be inspected after the run and conditionally alter the return code (bypassing failures for missing dependencies).

    When I try to use ubelt.cmd to launch the subprocess, however, it fails with this traceback:

    Traceback (most recent call last):
      File "<frozen runpy>", line 198, in _run_module_as_main
      File "<frozen runpy>", line 88, in _run_code
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\site-packages\safety-tox.py", line 72, in <module>
        __name__ == '__main__' and run(sys.argv[1:])
                                   ^^^^^^^^^^^^^^^^^
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\site-packages\safety-tox.py", line 69, in run
        raise SystemExit(Handler().run(args))
                         ^^^^^^^^^^^^^^^^^^^
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\site-packages\safety-tox.py", line 51, in run
        proc = self.runner(cmd)
               ^^^^^^^^^^^^^^^^
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\site-packages\jaraco\functools.py", line 35, in <lambda>
        return lambda *args, **kwargs: f1(f2(*args, **kwargs))
                                          ^^^^^^^^^^^^^^^^^^^
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\site-packages\ubelt\util_cmd.py", line 316, in cmd
        proc = make_proc()
               ^^^^^^^^^^^
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\site-packages\ubelt\util_cmd.py", line 293, in make_proc
        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\subprocess.py", line 1022, in __init__
        self._execute_child(args, executable, preexec_fn, close_fds,
      File "C:\hostedtoolcache\windows\Python\3.11.0\x64\Lib\subprocess.py", line 1491, in _execute_child
        hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    FileNotFoundError: [WinError 2] The system cannot find the file specified
    

    It seems that the way ubelt is manipulating the arguments, it doesn't allow the command to execute the way it would naturally.

    I try to avoid shell=True and always pass a sequence of args for the command as I find that to be the most portable. Forcing a string for the cmd leads to issues like seen above.

    Is there any way to get the tee functionality from ubelt without any other manipulation?

    opened by jaraco 3
  • Remove incorrect wheel requirement from pyproject.toml

    Remove incorrect wheel requirement from pyproject.toml

    Remove the incorrect wheel dependency, as it is added by the backend automatically. Listing it explicitly in the documentation was a historical mistake and has been fixed since, see: https://github.com/pypa/setuptools/commit/f7d30a9529378cf69054b5176249e5457aaf640a

    opened by mgorny 3
  • Dict regex

    Dict regex

    Hello!

    I quite like this package and your code. I also like regex, and I needed this, so I thought I might as well do a PR.

    I might do some other regex stuff later, so if you like it, I can do PRs from time to time.

    enhancement 
    opened by endremborza 3
  • Bump actions/setup-python from 4.3.0 to 4.3.1

    Bump actions/setup-python from 4.3.0 to 4.3.1

    Bumps actions/setup-python from 4.3.0 to 4.3.1.

    Release notes

    Sourced from actions/setup-python's releases.

    Improve error handling and messages

    In scope of this release we added improved error message to put operating system and its version in the logs (actions/setup-python#559). Besides, the release

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 2
  • help to confirm the depends of the module

    help to confirm the depends of the module

    Describe the bug This is not bug, just one confirmation.

    I am packaging the ubelt into Debian, everything is well but when run test cases I got:

    removing build/bdist.linux-x86_64/wheel
    * Building wheel...
    Successfully built ubelt-1.2.3-py3-none-any.whl
    I: pybuild plugin_pyproject:118: Unpacking wheel built for python3.10 with "installer" module
       dh_auto_test -O--buildsystem=pybuild
    I: pybuild base:240: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.11/build; python3.11 -m pytest tests
    ERROR: usage: __main__.py [options] [file_or_dir] [file_or_dir] [...]
    __main__.py: error: unrecognized arguments: --xdoctest --xdoctest-style=google
      inifile: /<<PKGBUILDDIR>>/pyproject.toml
      rootdir: /<<PKGBUILDDIR>>
    
    E: pybuild pybuild:379: test: plugin pyproject failed with: exit code=4: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.11/build; python3.11 -m pytest tests
    I: pybuild base:240: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.10/build; python3.10 -m pytest tests
    ERROR: usage: __main__.py [options] [file_or_dir] [file_or_dir] [...]
    __main__.py: error: unrecognized arguments: --xdoctest --xdoctest-style=google
      inifile: /<<PKGBUILDDIR>>/pyproject.toml
      rootdir: /<<PKGBUILDDIR>>
    
    E: pybuild pybuild:379: test: plugin pyproject failed with: exit code=4: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.10/build; python3.10 -m pytest tests
    dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.11 3.10" returned exit code 13
    make: *** [debian/rules:4: binary] Error 25
    dpkg-buildpackage: error: debian/rules binary subprocess returned exit status 2
    
    

    Please help me to confirm the xdoctest module is the repo? If so, I have to package it first to meet the requierment. thanks.

    opened by yuzibo 2
  • Bump actions/download-artifact from 2 to 3

    Bump actions/download-artifact from 2 to 3

    Bumps actions/download-artifact from 2 to 3.

    Release notes

    Sourced from actions/download-artifact's releases.

    v3.0.0

    What's Changed

    Breaking Changes

    With the update to Node 16, all scripts will now be run with Node 16 rather than Node 12.

    v2.1.0 Download Artifact

    • Improved output & logging
    • Fixed issue where downloading all artifacts could cause display percentages to be over 100%
    • Various small bug fixes & improvements

    v2.0.10

    • Retry on HTTP 500 responses from the service

    v2.0.9

    • Fixes to proxy related issues

    v2.0.8

    • Improvements to retryability if an error is encountered during artifact download

    v2.0.7 download-artifact

    • Improved download retry-ability if a partial download is encountered

    v2.0.6

    Update actions/core NPM package that is used internally

    v2.0.5

    • Add Third Party License Information

    v2.0.4

    • Use the latest version of the @actions/artifact NPM package

    v2.0.3

    • Misc improvements

    v2.0.2

    • Support for tilde expansion

    v2.0.1

    • Download path output
    • Improved logging
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 2
  • Bump codecov/codecov-action from 2.1.0 to 3.0.0

    Bump codecov/codecov-action from 2.1.0 to 3.0.0

    Bumps codecov/codecov-action from 2.1.0 to 3.0.0.

    Release notes

    Sourced from codecov/codecov-action's releases.

    v3.0.0

    Breaking Changes

    • #689 Bump to node16 and small fixes

    Features

    • #688 Incorporate gcov arguments for the Codecov uploader

    Dependencies

    • #548 build(deps-dev): bump jest-junit from 12.2.0 to 13.0.0
    • #603 [Snyk] Upgrade @​actions/core from 1.5.0 to 1.6.0
    • #628 build(deps): bump node-fetch from 2.6.1 to 3.1.1
    • #634 build(deps): bump node-fetch from 3.1.1 to 3.2.0
    • #636 build(deps): bump openpgp from 5.0.1 to 5.1.0
    • #652 build(deps-dev): bump @​vercel/ncc from 0.30.0 to 0.33.3
    • #653 build(deps-dev): bump @​types/node from 16.11.21 to 17.0.18
    • #659 build(deps-dev): bump @​types/jest from 27.4.0 to 27.4.1
    • #667 build(deps): bump actions/checkout from 2 to 3
    • #673 build(deps): bump node-fetch from 3.2.0 to 3.2.3
    • #683 build(deps): bump minimist from 1.2.5 to 1.2.6
    • #685 build(deps): bump @​actions/github from 5.0.0 to 5.0.1
    • #681 build(deps-dev): bump @​types/node from 17.0.18 to 17.0.23
    • #682 build(deps-dev): bump typescript from 4.5.5 to 4.6.3
    • #676 build(deps): bump @​actions/exec from 1.1.0 to 1.1.1
    • #675 build(deps): bump openpgp from 5.1.0 to 5.2.1
    Changelog

    Sourced from codecov/codecov-action's changelog.

    3.0.0

    Breaking Changes

    • #689 Bump to node16 and small fixes

    Features

    • #688 Incorporate gcov arguments for the Codecov uploader

    Dependencies

    • #548 build(deps-dev): bump jest-junit from 12.2.0 to 13.0.0
    • #603 [Snyk] Upgrade @​actions/core from 1.5.0 to 1.6.0
    • #628 build(deps): bump node-fetch from 2.6.1 to 3.1.1
    • #634 build(deps): bump node-fetch from 3.1.1 to 3.2.0
    • #636 build(deps): bump openpgp from 5.0.1 to 5.1.0
    • #652 build(deps-dev): bump @​vercel/ncc from 0.30.0 to 0.33.3
    • #653 build(deps-dev): bump @​types/node from 16.11.21 to 17.0.18
    • #659 build(deps-dev): bump @​types/jest from 27.4.0 to 27.4.1
    • #667 build(deps): bump actions/checkout from 2 to 3
    • #673 build(deps): bump node-fetch from 3.2.0 to 3.2.3
    • #683 build(deps): bump minimist from 1.2.5 to 1.2.6
    • #685 build(deps): bump @​actions/github from 5.0.0 to 5.0.1
    • #681 build(deps-dev): bump @​types/node from 17.0.18 to 17.0.23
    • #682 build(deps-dev): bump typescript from 4.5.5 to 4.6.3
    • #676 build(deps): bump @​actions/exec from 1.1.0 to 1.1.1
    • #675 build(deps): bump openpgp from 5.1.0 to 5.2.1
    Commits
    • e3c5604 Merge pull request #689 from codecov/feat/gcov
    • 174efc5 Update package-lock.json
    • 6243a75 bump to 3.0.0
    • 0d6466f Bump to node16
    • d4729ee fetch.default
    • 351baf6 fix: bash
    • d8cf680 Merge pull request #675 from codecov/dependabot/npm_and_yarn/openpgp-5.2.1
    • b775e90 Merge pull request #676 from codecov/dependabot/npm_and_yarn/actions/exec-1.1.1
    • 2ebc2f0 Merge pull request #682 from codecov/dependabot/npm_and_yarn/typescript-4.6.3
    • 8e2ef2b Merge pull request #681 from codecov/dependabot/npm_and_yarn/types/node-17.0.23
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 2
  • Bump actions/checkout from 2 to 3

    Bump actions/checkout from 2 to 3

    Bumps actions/checkout from 2 to 3.

    Release notes

    Sourced from actions/checkout's releases.

    v3.0.0

    • Update default runtime to node16

    v2.4.0

    • Convert SSH URLs like org-<ORG_ID>@github.com: to https://github.com/ - pr

    v2.3.5

    Update dependencies

    v2.3.4

    v2.3.3

    v2.3.2

    Add Third Party License Information to Dist Files

    v2.3.1

    Fix default branch resolution for .wiki and when using SSH

    v2.3.0

    Fallback to the default branch

    v2.2.0

    Fetch all history for all tags and branches when fetch-depth=0

    v2.1.1

    Changes to support GHES (here and here)

    v2.1.0

    Changelog

    Sourced from actions/checkout's changelog.

    Changelog

    v2.3.1

    v2.3.0

    v2.2.0

    v2.1.1

    • Changes to support GHES (here and here)

    v2.1.0

    v2.0.0

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 2
  • Bump actions/setup-python from 4.3.0 to 4.4.0

    Bump actions/setup-python from 4.3.0 to 4.4.0

    Bumps actions/setup-python from 4.3.0 to 4.4.0.

    Release notes

    Sourced from actions/setup-python's releases.

    Add support to install multiple python versions

    In scope of this release we added support to install multiple python versions. For this you can try to use this snippet:

        - uses: actions/setup-python@v4
          with:
            python-version: |
                3.8
                3.9
                3.10
    

    Besides, we changed logic with throwing the error for GHES if cache is unavailable to warn (actions/setup-python#566).

    Improve error handling and messages

    In scope of this release we added improved error message to put operating system and its version in the logs (actions/setup-python#559). Besides, the release

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • Provide a subprocess-compatible interface in cmd

    Provide a subprocess-compatible interface in cmd

    I was exploring using subprocess_tee when I found it doesn't support Windows and has issues with Python 3.12.

    When I tried dropping in ubelt.cmd as a replacement, I found it has a very different interface than subprocess, returning a dict instead of an object and having different keys.

    It would be nice if the result from command was an object similar to the one returned by subprocess.Popen (or even identical), so that code like this can readily switch between the implementations.

    opened by jaraco 1
  • 1.2.3: The test_deprecated_grabdata_args test fails

    1.2.3: The test_deprecated_grabdata_args test fails

    Describe the bug

    ========================================================================================== FAILURES ==========================================================================================
    _______________________________________________________________________________ test_deprecated_grabdata_args ________________________________________________________________________________
    
        def test_deprecated_grabdata_args():
            with pytest.warns(DeprecationWarning):
                import hashlib
                url = _demo_url()
                # dpath = ub.Path.appdir('ubelt/tests/test_download').ensuredir()
                # fname = basename(url)
                # fpath = join(dpath, fname)
    >           got_fpath = ub.grabdata(
                    url, hash_prefix='e09c80c42fda55f9d992e59ca6b3307d',
                    hasher=hashlib.md5())
    
    tests/test_download.py:383: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    ubelt/util_download.py:444: in grabdata
        stamp.renew()
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <ubelt.util_cache.CacheStamp object at 0x904747d00>, cfgstr = None, product = None
    
        def renew(self, cfgstr=None, product=None):
            """
            Recertify that the product has been recomputed by writing a new
            certificate to disk.
        
            Returns:
                None | dict: certificate information if enabled otherwise None.
        
            Example:
                >>> # Test that renew does nothing when the cacher is disabled
                >>> import ubelt as ub
                >>> dpath = ub.Path.appdir('ubelt/tests/cache-stamp-renew').ensuredir()
                >>> self = ub.CacheStamp('foo', dpath=dpath, enabled=False)
                >>> assert self.renew() is None
            """
            if not self.cacher.enabled:
                return None
            if cfgstr is not None:  # nocover
                from ubelt import schedule_deprecation
                schedule_deprecation(
                    modname='ubelt',
                    migration='Do not pass cfgstr to renew. Use the class depends arg',
                    name='cfgstr', type='CacheStamp.renew arg',
                    deprecate='1.1.0', error='1.3.0', remove='1.4.0',
                )
            if product is not None:  # nocover
                from ubelt import schedule_deprecation
                schedule_deprecation(
                    modname='ubelt',
                    migration='Do not pass product to renew. Use the class product arg',
                    name='product', type='CacheStamp.renew arg',
                    deprecate='1.1.0', error='1.3.0', remove='1.4.0',
                )
            certificate = self._new_certificate(cfgstr, product)
            err = self._check_certificate_hashes(certificate)
            if err:
    >           raise RuntimeError(err)
    E           RuntimeError: hash_prefix_mismatch
    
    ubelt/util_cache.py:1211: RuntimeError
    ------------------------------------------------------------------------------------ Captured stdout call ------------------------------------------------------------------------------------
    [cacher] ... file_10_0.txt.stamp cache miss
    [cacher] stamp expired no_cert
    Downloading url='http://localhost:17766/file_10_0.txt' to fpath='/disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/.cache/ubelt/file_10_0.txt'
     10/10... rate=129294.24 Hz, eta=0:00:00, total=0:00:00
    invalid hash prefix value (expected "e09c80c42fda55f9d992e59ca6b3307d", got "22d42eb002cefa81e9ad604ea57bc01d")
    ====================================================================================== warnings summary ======================================================================================
    ../../../../../../usr/local/lib/python3.9/site-packages/pytest_freezegun.py:17
      /usr/local/lib/python3.9/site-packages/pytest_freezegun.py:17: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
        if LooseVersion(pytest.__version__) < LooseVersion('3.6.0'):
    
    tests/test_pathlib.py::test_move_meta
    tests/test_pathlib.py::test_move_basic
    tests/test_pathlib.py::test_move_dir_to_existing_dir_noconflict
    tests/test_pathlib.py::test_move_dir_to_existing_dir_withconflict
    tests/test_pathlib.py::test_move_dir_to_non_existing
    tests/test_pathlib.py::test_move_to_nested_non_existing
      /disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.3/ubelt/util_path.py:1384: UserWarning: The ub.Path.move function is experimental and may change! Do not rely on this behavior yet!
        warnings.warn('The ub.Path.move function is experimental and may change! '
    
    tests/test_pathlib.py::test_copy_dir_to_existing_dir_withconflict
    tests/test_pathlib.py::test_copy_meta
    tests/test_pathlib.py::test_copy_basic
    tests/test_pathlib.py::test_copy_to_nested_non_existing_with_different_symlink_flags
    tests/test_pathlib.py::test_copy_dir_to_non_existing
    tests/test_pathlib.py::test_copy_dir_to_existing_dir_noconflict
      /disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.3/ubelt/util_path.py:1291: UserWarning: The ub.Path.copy function is experimental and may change, in corner cases. Primary cases seem stable.
        warnings.warn('The ub.Path.copy function is experimental and may change, '
    
    tests/test_indexable.py::test_indexable_walker_map_patterns
      /disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.3/ubelt/util_indexable.py:506: DeprecationWarning: The "indexable_allclose" function was deprecated, will cause an error and will be removed. The current version is 1.2.3. Use `ub.IndexableWalker(items1).allclose(ub.IndexableWalker(items2))` instead
        ub.schedule_deprecation(
    
    tests/test_path.py::test_tempdir
    tests/test_import.py::test_import_modpath_basic
    tests/test_import.py::test_package_submodules
    tests/test_import.py::test_import_modpath_package
    tests/test_import.py::test_modpath_to_modname
    tests/test_import.py::test_modname_to_modpath_package
    tests/test_import.py::test_modname_to_modpath_single
    tests/test_import.py::test_modname_to_modpath_namespace
      /disk-samsung/freebsd-ports/devel/py-ubelt/work-py39/ubelt-1.2.3/ubelt/util_path.py:368: DeprecationWarning: The "TempDir" class was deprecated in 1.2.0, will cause an error in 1.4.0 and will be removed in 1.5.0. The current version is 1.2.3. Use tempfile instead
        ub.schedule_deprecation(
    
    -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
    ================================================================================== short test summary info ===================================================================================
    SKIPPED [1] tests/test_futures.py:42: long test, demos that timeout does not work with SerialExecutor
    SKIPPED [1] tests/test_editable_modules.py:415: UBELT_DO_EDITABLE_TESTS is not enabled
    SKIPPED [1] tests/test_hash.py:435: blake3 is not available
    SKIPPED [1] tests/test_download.py:256: This takes a long time to timeout and I dont understand why
    =================================================================== 1 failed, 202 passed, 4 skipped, 22 warnings in 5.90s ====================================================================
    

    Desktop (please complete the following information):

    • OS: FreeBSD 13.1
    • Ubelt version 1.2.3
    • Python version 3.9
    opened by yurivict 1
  • Investigate code scanning alert - is this a real vulnerability?

    Investigate code scanning alert - is this a real vulnerability?

    A code scanner popped up with this as a potential vulnerability. I'm not sure if logging the hash of a file to stdout is leaking anything of relevance. I don't see how it is sensitive information. But if someone can point out if this is a vulnerability, and if so, why? Then, we can remove the log message. But I've found this very useful when establishing the initial hash of expected data (which itself mitigates a security issue!). But this is still UX, so if this somehow is sensitive, then It would be helpful if someone could explain or ellaborate.

    Tracking issue for:

    • [ ] https://github.com/Erotemic/ubelt/security/code-scanning/1
    opened by Erotemic 0
Releases(v1.2.3)
  • v1.2.3(Dec 3, 2022)

    Added

    • Support for FreeBSD in util_platform
    • ub.Path.copy and ub.Path.move
    • Tentative Python 3.11 support
    • ub.utext which is a repr2 with new defaults.
    • ub.IndexableWalker.allclose method to replace indexable_allclose

    Changed

    • ub.schedule_deprecation can now accept the strings "soon" or "now" for "when" arguments.
    • ub.schedule_deprecation can now accept stacklevel as as an argument.
    • ub.Path.apppdir can now be called without arguments.
    • Deprecate TempDir
    • ub.Timer can now accept ns as a keyword to enable nanosecond resolution.
    • ProgIter.format_message return value has changed.
    • ub.Path.mkdir now returns itself
    • Speedup dict_hist in the basic case by 2x

    Fixed

    • Issue in indexable_allclose where tolerances were not respected.
    • Issue in modname_to_modpath with exclude and editable installs.
    • Incompatibility with pathlib in PythonPathContext
    • Fixed issue in progiter with line clears
    • Issue in import_module_from_modname with editable installs where it would try to match modules that had the requested module as a prefix.
    • The timeout argument is now respected in the thread / process case of JobPool
    Source code(tar.gz)
    Source code(zip)
  • v1.2.2(Sep 5, 2022)

    Added

    • Add ubelt.util_dict.UDict.take.
    • Added __add__, __radd__, startswith, and endswith methods to ubelt.Path to make it a better drop-in replacement for string based paths.

    Changed

    • Reverse dunder methods of ubelt.SetDict now promote the type.
    • Add cls keyword argument to ubelt.SetDict methods.
    • Deprecate: ensure_app_cache_dir, ensure_app_config_dir, ensure_app_data_dir, get_app_cache_dir, get_app_config_dir, get_app_data_dir, readfrom, writeto, ensure_unicode.
    • Initial work on new Path methods for move and copy.
    • CacheStamp.renew now does nothing and returns None if the stamp is disabled.
    • AutoDict, SetDict, and UDict are now always ordered. In Python 3.6 it inherits from OrderedDict. Otherwise it uses regular dict, which will be ordered in 3.7+
    • AutoDict now inherits from UDict.
    • PathLike objects can now be used in args to ub.cmd when the command is an iterable.

    Deprecated

    • deprecate AutoOrderedDict, which is now indistinguishable from AutoDict

    Fixed

    • Tentative fixes for new __editable__ based install path with ub.modname_to_modpath
    Source code(tar.gz)
    Source code(zip)
  • v1.2.1(Aug 6, 2022)

  • v1.2.0(Aug 3, 2022)

    Version 1.2.0 - Released 2022-08-02

    Added

    • Added keywords argument to ub.compatible.
    • Added warncls argument to ub.schedule_deprecation.
    • Experimental SetDict, UDict and aliases sdict, udict.

    Fixed

    • Race condition on win32 in ubelt.symlink
    • Issue with ubelt.memoize_method where the method name and docstring were not wrapped correctly.
    • The timeout argument now works correctly with ub.cmd when tee=True.
    • Added appname to ubelt.download which was supposed to exist as indicated by the docs, but didn't.
    • The resources used by ubelt.cmd are now properly closed.

    Changed

    • ub.compatible no longer errors on positional only functions, instead it returns the keyword-compatible arguments.
    • An issue in ubelt.symlink with unintuitive behavior when an empty string was given as the link path. This now raises an error.
    • The main implementations of ubelt.sorted_vals and ubelt.map_vals were renamed to ubelt.sorted_values and ubelt.map_values, but the old names are still available as aliases.
    • Positional arguments in Path.augment have been modified.
    • In Path.augment, deprecate overloaded suffix and introduce stemsuffix as an alternative.
    • Added cls to a lot of util_dict funcs
    Source code(tar.gz)
    Source code(zip)
  • v1.1.2(Jul 1, 2022)

    Added

    • Added new module util_deprecate with the function schedule_deprecation, which is generally useful for library maintenance.

    Fixed

    • Fixed issue where ubelt Cacher triggered its own warnings
    • Fixed deprecated usage of LooseVersion

    Changed

    • Tentative deprecation or backwards incompatible change in ub.Path.augment with suffix or prefix keywords
    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Jun 10, 2022)

    Version 1.1.1 - Released 2022-06-09

    Changed

    • Removed warning from ubelt.Cacher when depends is not specified.
    • ub.timestamp / ub.timeparse now respects a default_timezone argument and handles datetime.date objects.
    • Type stubs are now included in the distribution

    Fixed

    • Issue #113, where a ub.find_exe test failed on Gentoo. Fixed by #114
    • Issue where older versions of CacheStamp would be interpreted as 1.1 stamps.
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Jun 4, 2022)

    Version 1.1.0 - Released 2022-06-03

    Added

    • New method: ub.timeparse can parse the result of ub.timestamp into a datetime object. Can optionally use dateutil.parser.parse under the hood.
    • ub.Path.ls a convenience function that aliases list(path.iterdir()).
    • ub.Path.walk to wrap os.walk.

    Changed

    • Register pathlib.Path with ub.repr2
    • Can now register global ub.repr2 extensions via ub.repr2.register
    • Can now register global ub.hash_data extensions via ub.hash_data.register
    • Removed deprecated arguments from ubelt.cmd.
    • ub.CacheStamp will now check the mtime and size to quickly check if the products have changed and force expiration.
    • ub.CacheStamp now takes an expires keyword arg, which will keep the cache valid only for the specified amount of time.
    • ub.CacheStamp now takes an hash_prefix keyword arg, which will check that it matches the hash of the product.
    • ub.cmd now has a system argument for modularity with os.system.
    • ub.cmd now accepts a timeout argument (tee support is pending).
    • ub.JobPool now contains a protected _prog variable allowing the user finer-grained progress controls.
    • ub.JobPool now contains a convenience method join that executes all jobs and returns a list of results.
    • ub.timestamp can now accept a datetime object as an argument, and will return the timestamp for that object.
    • The ubelt.util_download.grabdata function now uses CacheStamp instead of implementing its own stamp solution.
    • The ubelt.util_hash.HashableExtensions implementation was updated to use functools.singledispatch instead of the custom solution. This seems faster and should not have any API impact.

    Deprecated

    • product and cfgstr arguments to CacheStamp.expired
    • product and cfgstr arguments to CacheStamp.renew
    • Passing hasher as an instance to functions like grabdata or CacheStamp can cause unexpected hashes as they may be used more than once.

    Fixed

    • ub.hash_data now recognizes subclasses of registered types.
    • ub.timestamp() has been outputting incorrect (negated) UTC offsets. This is now fixed.
    • ub.timestamp() now works correctly when the year has less than 4 digits.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Feb 20, 2022)

    Version 1.0.1 - Released 2022-02-20

    Fixed

    • Bug where six was used but not listed as a dependency. Six is now removed as a dependency.
    • Fixed out of date docs in some places.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Feb 16, 2022)

    Version 1.0.0 - Released 2022-02-15

    Added

    • :func:ubelt.Path.appdir which functions like the get_app_*_dir methods in util_platform.
    • Add tail argument to :func:ubelt.Path.augment and :func:ubelt.util_path.augpath
    • Add json backend option to Cacher.

    Changed

    • IndexableWalker behavior has been changed, each time iter is called it resets its global state.
    • Remove support for Python 2.7 and Python 3.5
    • Removed deprecated functions scheduled for removal.
    • :func:ubelt.util_dict.dict_diff now preserves original dictionary order in Python 3.7+.
    • ub.hash_data can now hash slice objects.
    • INTENTION OF BREAKING CHANGE NOTIFICATION: ubelt.util_format.repr2 may no longer sort dictionaries by default. Looking into a backwards compatible way to work around this.
    Source code(tar.gz)
    Source code(zip)
  • v0.11.1(Jan 9, 2022)

    Version 0.11.1

    This is the last planned release for the 0.x branch of ubelt. Adds a few minor updates and doc fixes to the tools introduced in 0.11.0.

    The next 1.0.x branch will no longer support Python 2.7 and Python 3.5. This has a positive impact on import time:

    https://twitter.com/erotemic/status/1480069712981680128

    image

    Note that moving forward we may remove support for Python 3.x branches as they reach end of life without incrementing the major version. Python 3.6 end of life is (23 Dec 2021), and Python 3.7 end of life is (27 Jun 2023).

    Added

    • More ubelt.Path extensions for delete
    • Add timeout parameter to ubelt.download
    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Jan 3, 2022)

    Version 0.11.0 - Released 2022-01-03

    Added

    • Added ubelt.Path, as an extension and quicker-to-type version of pathlib.Path with extra functionality.
    • Added progkw as argument to JobPool.as_completed to control progress reporting
    • Added progkw as argument to ub.download / ub.grabdat to control progress reporting
    • Added util_zip with the zopen function. Access a file inside a zipfile with a standard open like interface.

    Fixed

    • ubelt.hash_data now handles non-numeric float values.
    • ubelt.chunks now works correctly when nchunks is specified.

    Changed

    • Changed default of _hashable_sequence types arg from True to False to be consistent, but kept existing types=True behavior in hashable extensions. Changes should be backwards compatible, but in the future we may introduce a breaking change to make hash behavior more consistent.
    Source code(tar.gz)
    Source code(zip)
  • v0.10.2(Dec 8, 2021)

    Version 0.10.2 - Released 2021-12-07

    Added

    • Added pyi type annotation files. (Used a custom script to export docstring type annotations)
    • Added default keyword argument to signature of ub.peek

    Fixed

    • Added map function to the executor classes.
    • ub.Executor now correctly returns itself from __enter__
    • Docstrings now have better type annotations
    • ProgIter had a bug in time_thresh, where it was never used (modified adjustment rules).
    • Fixed performance regression in ProgIter (now less overhead than tqdm)

    Changed

    • New CI GPG Keys: Erotemic-CI: 70858F4D01314BF21427676F3D568E6559A34380 for reference the old signing key was 98007794ED130347559354B1109AC852D297D757.
    • Verbose test from symlink previously showed "real -> link" which makes no sense because a link should be the object that "is pointing". Thus it now shows "link -> real"
    • ub.download should now generate less stdout text
    • New in-repo "dev" benchmarks
    Source code(tar.gz)
    Source code(zip)
  • 0.10.1(Aug 24, 2021)

  • 0.10.0(Aug 23, 2021)

    Added

    • new hashing 36-character base with alias (alphanum / abc123 / 36)
    • Added "compact" argument to ub.repr2
    • added candidate utilities: named_product, varied_values to util_dict
    • added candidate utilities: compatible to util_func
    • Added util_indexable and IndexableWalker (ported from kwcoco)
    • Added util_futures with ub.JobPool and ub.Executor (ported from kwcoco)
    • Added util_download_manager with simple implementation of ub.DownloadManager
    • Added candidate functions to ubelt proper

    Fixed

    • ubelt.download now errors earlier if the parent directory does not exist
    • PyTest no longer throws warnings
    • Fixed issue with download and ByteIO objects in 3.8
    • Bug in Python 3.8+ on win32 that did not account for the change on os.readlink behavior

    Changed

    • Modified corner cases in ub.repr2 to move towards behavior that is easier to reason about.

    • Remove support for Python 3.4

    Source code(tar.gz)
    Source code(zip)
  • 0.8.8(Jan 12, 2020)

Owner
Jon Crall
Staff R&D Engineer; Open Source Enthusiast
Jon Crall
A utility that makes it easy to work with Python projects containing lots of packages, of which you only want to develop some.

Mixed development source packages on top of stable constraints using pip mxdev [mɪks dɛv] is a utility that makes it easy to work with Python projects

BlueDynamics Alliance 6 Jun 8, 2022
EVE-NG tools, A Utility to make operations with EVE-NG more friendly.

EVE-NG tools, A Utility to make operations with EVE-NG more friendly. Also it support different snapshot operations with same style as Libvirt/KVM

Bassem Aly 8 Jan 5, 2023
A python package containing all the basic functions and classes for python. From simple addition to advanced file encryption.

A python package containing all the basic functions and classes for python. From simple addition to advanced file encryption.

PyBash 11 May 22, 2022
This is Cool Utility tools that you can use in python.

This is Cool Utility tools that you can use in python. There are a few tools that you might find very useful, you can use this on pretty much any project and some utils might help you a lot and save so much time since it’s a simple function.

Senarc Studios 6 Apr 18, 2022
A simple and easy to use Spam Bot made in Python!

This is a simple spam bot made in python. You can use to to spam anyone with anything on any platform.

null 7 Sep 8, 2022
🔩 Like builtins, but boltons. 250+ constructs, recipes, and snippets which extend (and rely on nothing but) the Python standard library. Nothing like Michael Bolton.

Boltons boltons should be builtins. Boltons is a set of over 230 BSD-licensed, pure-Python utilities in the same spirit as — and yet conspicuously mis

Mahmoud Hashemi 6k Jan 4, 2023
This utility synchronises spelling dictionaries from various tools with each other.

This utility synchronises spelling dictionaries from various tools with each other. This way the words that have been trained on MS Office are also correctly checked in vim or Firefox. And vice versa of course.

Patrice Neff 2 Feb 11, 2022
pydsinternals - A Python native library containing necessary classes, functions and structures to interact with Windows Active Directory.

pydsinternals - Directory Services Internals Library A Python native library containing necessary classes, functions and structures to interact with W

Podalirius 36 Dec 14, 2022
A repository containing several general purpose Python scripts to automate daily and common tasks.

General Purpose Scripts Introduction This repository holds a curated list of Python scripts which aim to help us automate daily and common tasks. You

GDSC RCCIIT 46 Dec 25, 2022
Local backup made easy, with Python and shutil

KTBackup BETA Local backup made easy, with Python and shutil Features One-command backup and restore Minimalistic (only using stdlib) Convenient direc

kelptaken 1 Dec 27, 2021
Regression Metrics Calculation Made easy

Regression Metrics Mean Absolute Error Mean Square Error Root Mean Square Error Root Mean Square Logarithmic Error Root Mean Square Logarithmic Error

Ashish Patel 12 Jan 2, 2023
isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections and by type.

isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections and by type. It provides a command line utility, Python library and plugins for various editors to quickly sort all your imports.

Python Code Quality Authority 5.5k Jan 8, 2023
A simple tool to move and rename Nvidia Share recordings to a more sensible format.

A simple tool to move and rename Nvidia Share recordings to a more sensible format.

Jasper Rebane 8 Dec 23, 2022
Python utility for discovering interesting CFPreferences values on iDevices

Description Simple utility to search for interesting preferences in iDevices. Installation python3 -m pip install -U --user cfprefsmon Example In this

null 12 Aug 19, 2022
Yet another retry utility in Python

Yet another retry utility in Python, avereno being the Malagasy word for retry.

Haute École d'Informatique de Madagascar 4 Nov 2, 2021
The git for the Python Story Utility Package library.

SUP The git for the Python Story Utility Package library. Installation: Install SUP by simply running pip install psup in your terminal. Check out our

Enoki 6 Nov 27, 2022
A collection of utility functions to prototype geometry processing research in python

gpytoolbox This repo is a work in progress and contains general utility functions I have needed to code while trying to work on geometry process resea

Silvia Sellán 73 Jan 6, 2023
A simple and easy to use collection of random python functions.

A simple and easy to use collection of random python functions.

Diwan Mohamed Faheer 1 Nov 17, 2021
Shypan, a simple, easy to use, full-featured library written in Python.

Shypan, a simple, easy to use, full-featured library written in Python.

ShypanLib 4 Dec 8, 2021