In the middle of the desert you can say anything you want
To access the #obsidian console, <C-S-i>
worked. It was the standard “Dev tools”.1
Since I seem to keep forgetting:
simple-scan
is the program I use to talk to scanners. You can select various options (scan document, photo etc).
Keeps #scanning in the exact same PDF document until you break it.
In #pycharm, “Pin tab” exists! But then it’s not “Tab 1” etc anymore and I can’t use my shortcuts
(Those too after a long talk to a colleague at work, this time #py/argparse)
Cool things about argparse
:1
parser.add_argument('--two-words')
would automatically map to args.two_words
(_
vs -
)!pathlib.Path()
works as expected, and even automagically parses string paths from args into the Path!
str or Path
ambiguity.“Be strict and clear from the very beginning, then you don’t have to deal Path or str”
parser.add_argument('a', type=argparse.FileType('w', encoding='latin-1'))
parser.add_argument('b', type=pathlib.Path)
os.environ()
! Then you can also run it as
WHATVEER_VALUE=234 python3 file.py
A nice structure for it all is:
if __name__ == '__main__':
runs a function like main()
getting rid of the scope issuesdef parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument('--input-directory' ..)
return parser.parse_args()
main()
we use it like args = parse_args(); if args.input_directory == ...
This is nice also because then we don’t have to deal with an arparse object in main, just its results.Also, in general, CLI programs have arguments like program --arg-one
, not program --arg_one
. I write the latter one because I still feel I’m in a python world, but Python would parse such dashed arguments into classic ones (see above). TODO look for some best practices for CLI programs, including Python ones, POSIX etc etc etc.
From a conversation with a colleague at work about #py/logging
Logger names can be used to cleanly output and separate them.
Assuming one has a package with multiple files/subfolders in it, it’s possible to give each one their own logger, like this:
In the main file of the package:
logger = logging.getLogger(__package__)
In all the other files:
logger = logging.getLogger(__name__)
That way paths ./package/my_module.py
lead to loggers named like package.my_module
that map the semantical and the directory structure.
In a setup above, one can then easily change the settings of the loggers referring to them by their names.
Configuring logging: Logging HOWTO — Python 3.10.0 documentation
Changing loglevel is easy from code,
if args.debug:
logger.setLevel(logging.DEBUG)
logging.config
allows to change the config from ini-like config files. Two main ways:
logging.config.fileConfig
reads ini-like config files,
logging.config.dictConfig
1 from dictionaries.
Sample .yaml that when converted to dict would change the loglevel of different loggers:
version: 1
loggers:
packageName.mymodule1:
level: DEBUG
packageName.mymodule2:
level: DEBUG
These loggers can even include external ones!
Short notes about #py/poetry for package management
poetry new packagename
creates a poetry project
From within the folder with the package:
poetry install
== pip3 install -r requierements.txt
poetry shell
== source .venv/bin/activate
exit
== deactivate
Basic usage | Documentation | Poetry - Python dependency management and packaging made easy:
{cache-dir}/virtualenvs
, which on my box is /home/me/.cache/pypoetry/virtualenvs/ptest-eeSDLvcF-py3.6/bin/activate
poetry.lock
caches the resolved packages once we install things once.
pyproject.toml
, a warning will be shown otherwisepoetry update
updates everything to the latest versions, overwriting poetry.lock
poetry init
initializes a project and creates a pyproject.toml
interactively, allowing even to search for packages etc!Adding packages:
poetry add yaml
adds a packagepoetry search yaml
looks for packages in remote repos! Will tell you that you actually want pyyaml
Providing a __main__.py
along with __init__.py
makes the package itself executable:
$ python -m module_name
__main__.py
would have an usual if __name__ == "__main__" block
and run stuff imported from other files of that package.
(From a python riddle at work)
Things declared in if __name__ == '__main__'
are in global scope. Not because it’s special, but because ..global scope. All these bugs go away if you move main()
to a separate function.
Code from SO answer:[^2]
In main:
>>> if __name__ == '__main__':
... x = 1
... print 'x' in globals()
True
Inside a function:
>>> def foo():
... if __name__ == '__main__':
... bar = 1
... foo()
... print 'bar' in globals()
False
Python doesn’t have block-local scope, so any variables you use inside an if block will be added to the closest enclosing “real” scope.
Someone mentioned that if __name__ == '__main__'
can happen anywhere in the code. Never thought about this
If sync is enabled, in settings -> Sync there’s a “Deleted files” with versions and actions.
If not, unless a setting is set to delete to Obsidian’s trash, it’s left to the filesystem, so trash can or extundelete
in my case or whatever.
Link by M.O.: nix-community/nix-data-science: Standard set of packages and overlays for data-scientists [maintainer=@tbenst]