config2py.util

Utility functions for config2py.

config2py.util.always_true(x: Any) bool[source]

Function that just returns True.

config2py.util.ask_user_for_input(prompt: str, default: str | None = None, *, mask_input=False, masking_toggle_str: str | None = None, egress: ~typing.Callable = <function identity>) str[source]

Ask the user for input, optionally masking, validating and transforming the input.

Parameters:
  • prompt – Prompt to display to the user

  • default – Default value to return if the user enters nothing

  • mask_input – Whether to mask the user’s input

  • masking_toggle_str – String to toggle input masking. If None, no toggle is available. If not None (a common choice is the empty string) the user can enter this string to toggle input masking.

  • egress – Function to apply to the user’s response before returning it. This can be used to validate the response, for example.

Returns:

The user’s response (or the default value if the user entered nothing)

config2py.util.extract_variable_declarations(string: str, expand: dict | bool | None = None) dict[source]

Reads the contents of a config file, extracting Unix-style environment variable declarations of the form export {NAME}={value}, returning a dictionary of {NAME: value, …} pairs.

See issue for more info and applications: https://github.com/i2mint/config2py/issues/2

Parameters:
  • string – String to extract variable declarations from

  • expand – An optional dictionary of variable names and values to use to expand variables that are referenced (i.e. $NAME is a reference to NAME variable) in the values of config variables. If True, expand is replaced with an empty dictionary, which means we want to expand variables recursively, but we have no references to seed the expansion with. If False, expand is replaced with None, indicating that we don’t want to expand any variables.

Returns:

A dictionary of variable names and values.

>>> config = 'export ENVIRONMENT="dev"\nexport PORT=8080\nexport DEBUG=true'
>>> extract_variable_declarations(config)
{'ENVIRONMENT': 'dev', 'PORT': '8080', 'DEBUG': 'true'}
>>> config = 'export PATH="$PATH:/usr/local/bin"\nexport EDITOR="nano"'
>>> extract_variable_declarations(config)
{'PATH': '$PATH:/usr/local/bin', 'EDITOR': 'nano'}

The expand argument can be used to expand variables in the values of other.

Let’s add a reference to the PATH variable in the EDITOR variable:

>>> config = 'export PATH="$PATH:/usr/local/bin"\nexport EDITOR="nano $PATH"'

If you specify a value for PATH in the expand argument, you’ll see it reflected in the PATH variable (self reference) and the EDITOR variable. (Note if you changed the order of PATH and EDITOR in the config, you wouldn’t get the same thing though.)

>>> extract_variable_declarations(config, expand={'PATH': '/root'})
{'PATH': '/root:/usr/local/bin', 'EDITOR': 'nano /root:/usr/local/bin'}

If you specify expand={}, the first PATH variable will not be expanded, since PATH is not in the expand dictionary. But the second PATH variable, referenced in the definition of EDITOR will be expanded, since it is in the expand dictionary.

>>> extract_variable_declarations(config, expand={})
{'PATH': '$PATH:/usr/local/bin', 'EDITOR': 'nano $PATH:/usr/local/bin'}
config2py.util.get_app_data_directory(app_name: str = 'config2py', *, setup_callback: ~typing.Callable[[str], None] = <function _default_folder_setup>, ensure_exists: bool = False) str

Retrieve or create the app data directory specific to the given app name.

Args: - app_name (str): Name of the app for which the data directory is needed. - setup_callback (Callable[[str], None]): A callback function to initialize the directory.

Default is _default_folder_setup.

  • ensure_exists (bool): Whether to ensure the directory exists.

Returns: - str: Path to the app data directory.

By default, the app will be “config2py”:

>>> get_app_data_folder()  
'.../.config/config2py'

You can specify a different app name though. And if you want, you can also specify a callback function to initialize the directory.

>>> path = get_app_data_folder('my_app', ensure_exists=True)  
>>> path  
'/Users/.../.config/my_app'
>>> os.path.exists(path)  

You can also specify a path relative to the app data root directory (on linux/mac systems, this is typically ~/.config)

>>> get_app_data_folder('another/app/and/subfolder')  
'/Users/.../.config/another/app/and/subfolder'
config2py.util.get_app_data_folder(app_name: str = 'config2py', *, setup_callback: ~typing.Callable[[str], None] = <function _default_folder_setup>, ensure_exists: bool = False) str[source]

Retrieve or create the app data directory specific to the given app name.

Args: - app_name (str): Name of the app for which the data directory is needed. - setup_callback (Callable[[str], None]): A callback function to initialize the directory.

Default is _default_folder_setup.

  • ensure_exists (bool): Whether to ensure the directory exists.

Returns: - str: Path to the app data directory.

By default, the app will be “config2py”:

>>> get_app_data_folder()  
'.../.config/config2py'

You can specify a different app name though. And if you want, you can also specify a callback function to initialize the directory.

>>> path = get_app_data_folder('my_app', ensure_exists=True)  
>>> path  
'/Users/.../.config/my_app'
>>> os.path.exists(path)  

You can also specify a path relative to the app data root directory (on linux/mac systems, this is typically ~/.config)

>>> get_app_data_folder('another/app/and/subfolder')  
'/Users/.../.config/another/app/and/subfolder'
config2py.util.get_app_data_rootdir(*, ensure_exists=False) str[source]

Returns the full path of a directory suitable for storing application-specific data.

On Windows, this is typically %APPDATA%. On macOS, this is typically ~/.config. On Linux, this is typically ~/.config.

Returns:

The full path of the app data folder.

Return type:

str

See https://github.com/i2mint/i2mint/issues/1.

>>> get_app_data_rootdir()  
'/Users/.../.config'

If ensure_exists is True, the folder will be created if it doesn’t exist.

>>> get_app_data_rootdir(ensure_exists=True)  
'/Users/.../.config'

Note: The default app data folder is the system default for the current operating system. If you want to override this, you can do so by setting the CONFIG2PY_APP_DATA_FOLDER environment variable to the path of the folder you want to use.

config2py.util.get_configs_directory_for_app(app_name: str = 'config2py', *, configs_name: str = 'configs', app_dir_setup_callback: ~typing.Callable[[str], None] = <function _default_folder_setup>, config_dir_setup_callback: ~typing.Callable[[str], None] = <function _default_folder_setup>)

Retrieve or create the configs directory specific to the given app name.

Args: - app_name (str): Name of the app for which the configs directory is needed. - configs_name (str): Name of the configs directory. - app_dir_setup_callback (Callable[[str], None]): A callback function to initialize the app directory.

Default is _default_folder_setup.

  • config_dir_setup_callback (Callable[[str], None]): A callback function to initialize the configs directory.

    Default is _default_folder_setup.

config2py.util.get_configs_folder_for_app(app_name: str = 'config2py', *, configs_name: str = 'configs', app_dir_setup_callback: ~typing.Callable[[str], None] = <function _default_folder_setup>, config_dir_setup_callback: ~typing.Callable[[str], None] = <function _default_folder_setup>)[source]

Retrieve or create the configs directory specific to the given app name.

Args: - app_name (str): Name of the app for which the configs directory is needed. - configs_name (str): Name of the configs directory. - app_dir_setup_callback (Callable[[str], None]): A callback function to initialize the app directory.

Default is _default_folder_setup.

  • config_dir_setup_callback (Callable[[str], None]): A callback function to initialize the configs directory.

    Default is _default_folder_setup.

config2py.util.identity(x: Any) Any[source]

Function that just returns its argument.

config2py.util.is_not_empty(x: Any) bool[source]

Function that returns True if x is not empty.

config2py.util.is_repl()[source]

Determines if the Python interpreter is running in REPL.

To test: If you put it in a module.py, do a print of it in the module, and do python module.py it should print False. If you do python -i module.py, or call it from a python console or jupyter notebook, it should return True.

Parameters:

repl_conditions (list) – A list of functions that return True if the interpreter is running in a REPL, False otherwise. By default, this is a list of two functions that check if: - get_ipython is in globals - __main__ does not have a __file__ attribute

Returns:

True if running in a REPL, False otherwise.

Return type:

bool

is_repl.repl_conditions is a set of functions that return True if the interpreter. This set can be modified to modify the behavior of is_repl.

config2py.util.parse_assignments_from_py_source(source_code: str, *, name_filt=None, value_filt=<function _value_node_is_instance_of>)[source]

Parse assignments from python source code.

>>> source_code = '''a = 1
... b = 'hello'
... c = [1, 2, 3]
... def func():
...     d = 4
... '''
>>> dict(parse_assignments_from_py_source(source_code))
{'a': 1, 'b': 'hello', 'c': [1, 2, 3], 'd': 4}
config2py.util.process_path(*path: Iterable[str], ensure_dir_exists=False, assert_exists=False, ensure_endswith_slash=False, ensure_does_not_end_with_slash=False, expanduser=True, rootdir: str = '') str[source]

Process a path string, ensuring it exists, and optionally expanding user.

Parameters:
  • path (Iterable[str]) – The path to process. Can be multiple components of a path.

  • ensure_dir_exists (bool) – Whether to ensure the path exists.

  • assert_exists (bool) – Whether to assert that the path exists.

  • ensure_endswith_slash (bool) – Whether to ensure the path ends with a slash.

  • ensure_does_not_end_with_slash (bool) – Whether to ensure the path does not end with a slash.

  • expanduser (bool) – Whether to expand the user in the path.

Returns:

The processed path.

Return type:

str

>>> process_path('a', 'b', 'c')
'a/b/c'
>>> from functools import partial
>>> process_path('a', 'b', 'c', rootdir='/root/dir/', ensure_endswith_slash=True)
'/root/dir/a/b/c/'