Showing preview only (1,075K chars total). Download the full file or copy to clipboard to get everything.
Repository: Delgan/loguru
Branch: master
Commit: 8c7d33c4cd34
Files: 305
Total size: 993.1 KB
Directory structure:
gitextract_f3vpu0bp/
├── .codecov.yml
├── .github/
│ ├── SECURITY.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── codeql-analysis.yml
│ ├── docs.yml
│ ├── lint.yml
│ ├── packaging.yml
│ └── tests.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yml
├── .vscode/
│ └── settings.json
├── CHANGELOG.rst
├── CONTRIBUTING.rst
├── LICENSE
├── README.md
├── docs/
│ ├── Makefile
│ ├── _static/
│ │ ├── css/
│ │ │ └── loguru.css
│ │ └── js/
│ │ └── copybutton.js
│ ├── _templates/
│ │ ├── breadcrumbs.html
│ │ └── layout.html
│ ├── api/
│ │ ├── logger.rst
│ │ ├── type_hints.rst
│ │ └── type_hints_source.rst
│ ├── api.rst
│ ├── conf.py
│ ├── index.rst
│ ├── overview.rst
│ ├── project/
│ │ ├── changelog.rst
│ │ ├── contributing.rst
│ │ └── license.rst
│ ├── project.rst
│ ├── resources/
│ │ ├── migration.rst
│ │ ├── recipes.rst
│ │ └── troubleshooting.rst
│ └── resources.rst
├── loguru/
│ ├── __init__.py
│ ├── __init__.pyi
│ ├── _asyncio_loop.py
│ ├── _better_exceptions.py
│ ├── _colorama.py
│ ├── _colorizer.py
│ ├── _contextvars.py
│ ├── _ctime_functions.py
│ ├── _datetime.py
│ ├── _defaults.py
│ ├── _error_interceptor.py
│ ├── _file_sink.py
│ ├── _filters.py
│ ├── _get_frame.py
│ ├── _handler.py
│ ├── _locks_machinery.py
│ ├── _logger.py
│ ├── _recattrs.py
│ ├── _simple_sinks.py
│ ├── _string_parsers.py
│ └── py.typed
├── pyproject.toml
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ ├── exceptions/
│ │ ├── output/
│ │ │ ├── backtrace/
│ │ │ │ ├── chained_expression_direct.txt
│ │ │ │ ├── chained_expression_indirect.txt
│ │ │ │ ├── chaining_first.txt
│ │ │ │ ├── chaining_second.txt
│ │ │ │ ├── chaining_third.txt
│ │ │ │ ├── enqueue.txt
│ │ │ │ ├── enqueue_with_others_handlers.txt
│ │ │ │ ├── frame_values_backward.txt
│ │ │ │ ├── frame_values_forward.txt
│ │ │ │ ├── function.txt
│ │ │ │ ├── head_recursion.txt
│ │ │ │ ├── missing_attributes_traceback_objects.txt
│ │ │ │ ├── missing_lineno_frame_objects.txt
│ │ │ │ ├── nested.txt
│ │ │ │ ├── nested_chained_catch_up.txt
│ │ │ │ ├── nested_decorator_catch_up.txt
│ │ │ │ ├── nested_explicit_catch_up.txt
│ │ │ │ ├── nested_wrapping.txt
│ │ │ │ ├── no_tb.txt
│ │ │ │ ├── not_enough_arguments.txt
│ │ │ │ ├── raising_recursion.txt
│ │ │ │ ├── suppressed_expression_direct.txt
│ │ │ │ ├── suppressed_expression_indirect.txt
│ │ │ │ ├── tail_recursion.txt
│ │ │ │ └── too_many_arguments.txt
│ │ │ ├── diagnose/
│ │ │ │ ├── assertion_error.txt
│ │ │ │ ├── assertion_error_custom.txt
│ │ │ │ ├── assertion_error_in_string.txt
│ │ │ │ ├── attributes.txt
│ │ │ │ ├── chained_both.txt
│ │ │ │ ├── encoding.txt
│ │ │ │ ├── global_variable.txt
│ │ │ │ ├── indentation_error.txt
│ │ │ │ ├── keyword_argument.txt
│ │ │ │ ├── multilines_repr.txt
│ │ │ │ ├── no_error_message.txt
│ │ │ │ ├── parenthesis.txt
│ │ │ │ ├── source_multilines.txt
│ │ │ │ ├── source_strings.txt
│ │ │ │ ├── syntax_error.txt
│ │ │ │ ├── syntax_highlighting.txt
│ │ │ │ ├── truncating.txt
│ │ │ │ └── unprintable_object.txt
│ │ │ ├── modern/
│ │ │ │ ├── decorate_async_generator.txt
│ │ │ │ ├── exception_formatting_async_generator.txt
│ │ │ │ ├── exception_group_catch.txt
│ │ │ │ ├── f_string.txt
│ │ │ │ ├── grouped_as_cause_and_context.txt
│ │ │ │ ├── grouped_max_depth.txt
│ │ │ │ ├── grouped_max_length.txt
│ │ │ │ ├── grouped_nested.txt
│ │ │ │ ├── grouped_simple.txt
│ │ │ │ ├── grouped_with_cause_and_context.txt
│ │ │ │ ├── match_statement.txt
│ │ │ │ ├── notes.txt
│ │ │ │ ├── positional_only_argument.txt
│ │ │ │ ├── type_hints.txt
│ │ │ │ └── walrus_operator.txt
│ │ │ ├── others/
│ │ │ │ ├── assertionerror_without_traceback.txt
│ │ │ │ ├── broken_but_decorated_repr.txt
│ │ │ │ ├── catch_as_context_manager.txt
│ │ │ │ ├── catch_as_decorator_with_parentheses.txt
│ │ │ │ ├── catch_as_decorator_without_parentheses.txt
│ │ │ │ ├── catch_as_function.txt
│ │ │ │ ├── catch_message.txt
│ │ │ │ ├── exception_formatting_coroutine.txt
│ │ │ │ ├── exception_formatting_function.txt
│ │ │ │ ├── exception_formatting_generator.txt
│ │ │ │ ├── exception_in_property.txt
│ │ │ │ ├── handler_formatting_with_context_manager.txt
│ │ │ │ ├── handler_formatting_with_decorator.txt
│ │ │ │ ├── level_name.txt
│ │ │ │ ├── level_number.txt
│ │ │ │ ├── message_formatting_with_context_manager.txt
│ │ │ │ ├── message_formatting_with_decorator.txt
│ │ │ │ ├── nested_with_reraise.txt
│ │ │ │ ├── one_liner_recursion.txt
│ │ │ │ ├── recursion_error.txt
│ │ │ │ ├── repeated_lines.txt
│ │ │ │ ├── syntaxerror_without_traceback.txt
│ │ │ │ ├── sys_tracebacklimit.txt
│ │ │ │ ├── sys_tracebacklimit_negative.txt
│ │ │ │ ├── sys_tracebacklimit_none.txt
│ │ │ │ ├── sys_tracebacklimit_unset.txt
│ │ │ │ └── zerodivisionerror_without_traceback.txt
│ │ │ └── ownership/
│ │ │ ├── assertion_from_lib.txt
│ │ │ ├── assertion_from_local.txt
│ │ │ ├── callback.txt
│ │ │ ├── catch_decorator.txt
│ │ │ ├── catch_decorator_from_lib.txt
│ │ │ ├── decorated_callback.txt
│ │ │ ├── direct.txt
│ │ │ ├── indirect.txt
│ │ │ ├── string_lib.txt
│ │ │ ├── string_source.txt
│ │ │ └── syntaxerror.txt
│ │ └── source/
│ │ ├── backtrace/
│ │ │ ├── chained_expression_direct.py
│ │ │ ├── chained_expression_indirect.py
│ │ │ ├── chaining_first.py
│ │ │ ├── chaining_second.py
│ │ │ ├── chaining_third.py
│ │ │ ├── enqueue.py
│ │ │ ├── enqueue_with_others_handlers.py
│ │ │ ├── frame_values_backward.py
│ │ │ ├── frame_values_forward.py
│ │ │ ├── function.py
│ │ │ ├── head_recursion.py
│ │ │ ├── missing_attributes_traceback_objects.py
│ │ │ ├── missing_lineno_frame_objects.py
│ │ │ ├── nested.py
│ │ │ ├── nested_chained_catch_up.py
│ │ │ ├── nested_decorator_catch_up.py
│ │ │ ├── nested_explicit_catch_up.py
│ │ │ ├── nested_wrapping.py
│ │ │ ├── no_tb.py
│ │ │ ├── not_enough_arguments.py
│ │ │ ├── raising_recursion.py
│ │ │ ├── suppressed_expression_direct.py
│ │ │ ├── suppressed_expression_indirect.py
│ │ │ ├── tail_recursion.py
│ │ │ └── too_many_arguments.py
│ │ ├── diagnose/
│ │ │ ├── assertion_error.py
│ │ │ ├── assertion_error_custom.py
│ │ │ ├── assertion_error_in_string.py
│ │ │ ├── attributes.py
│ │ │ ├── chained_both.py
│ │ │ ├── encoding.py
│ │ │ ├── global_variable.py
│ │ │ ├── indentation_error.py
│ │ │ ├── keyword_argument.py
│ │ │ ├── multilines_repr.py
│ │ │ ├── no_error_message.py
│ │ │ ├── parenthesis.py
│ │ │ ├── source_multilines.py
│ │ │ ├── source_strings.py
│ │ │ ├── syntax_error.py
│ │ │ ├── syntax_highlighting.py
│ │ │ ├── truncating.py
│ │ │ └── unprintable_object.py
│ │ ├── modern/
│ │ │ ├── decorate_async_generator.py
│ │ │ ├── exception_formatting_async_generator.py
│ │ │ ├── exception_group_catch.py
│ │ │ ├── f_string.py
│ │ │ ├── grouped_as_cause_and_context.py
│ │ │ ├── grouped_max_depth.py
│ │ │ ├── grouped_max_length.py
│ │ │ ├── grouped_nested.py
│ │ │ ├── grouped_simple.py
│ │ │ ├── grouped_with_cause_and_context.py
│ │ │ ├── match_statement.py
│ │ │ ├── notes.py
│ │ │ ├── positional_only_argument.py
│ │ │ ├── type_hints.py
│ │ │ └── walrus_operator.py
│ │ ├── others/
│ │ │ ├── assertionerror_without_traceback.py
│ │ │ ├── broken_but_decorated_repr.py
│ │ │ ├── catch_as_context_manager.py
│ │ │ ├── catch_as_decorator_with_parentheses.py
│ │ │ ├── catch_as_decorator_without_parentheses.py
│ │ │ ├── catch_as_function.py
│ │ │ ├── catch_message.py
│ │ │ ├── exception_formatting_coroutine.py
│ │ │ ├── exception_formatting_function.py
│ │ │ ├── exception_formatting_generator.py
│ │ │ ├── exception_in_property.py
│ │ │ ├── handler_formatting_with_context_manager.py
│ │ │ ├── handler_formatting_with_decorator.py
│ │ │ ├── level_name.py
│ │ │ ├── level_number.py
│ │ │ ├── message_formatting_with_context_manager.py
│ │ │ ├── message_formatting_with_decorator.py
│ │ │ ├── nested_with_reraise.py
│ │ │ ├── one_liner_recursion.py
│ │ │ ├── recursion_error.py
│ │ │ ├── repeated_lines.py
│ │ │ ├── syntaxerror_without_traceback.py
│ │ │ ├── sys_tracebacklimit.py
│ │ │ ├── sys_tracebacklimit_negative.py
│ │ │ ├── sys_tracebacklimit_none.py
│ │ │ ├── sys_tracebacklimit_unset.py
│ │ │ └── zerodivisionerror_without_traceback.py
│ │ └── ownership/
│ │ ├── _init.py
│ │ ├── assertion_from_lib.py
│ │ ├── assertion_from_local.py
│ │ ├── callback.py
│ │ ├── catch_decorator.py
│ │ ├── catch_decorator_from_lib.py
│ │ ├── decorated_callback.py
│ │ ├── direct.py
│ │ ├── indirect.py
│ │ ├── string_lib.py
│ │ ├── string_source.py
│ │ ├── syntaxerror.py
│ │ └── usersite/
│ │ └── somelib/
│ │ └── __init__.py
│ ├── test_activation.py
│ ├── test_add_option_backtrace.py
│ ├── test_add_option_catch.py
│ ├── test_add_option_colorize.py
│ ├── test_add_option_context.py
│ ├── test_add_option_diagnose.py
│ ├── test_add_option_enqueue.py
│ ├── test_add_option_filter.py
│ ├── test_add_option_format.py
│ ├── test_add_option_kwargs.py
│ ├── test_add_option_level.py
│ ├── test_add_option_serialize.py
│ ├── test_add_sinks.py
│ ├── test_ansimarkup_basic.py
│ ├── test_ansimarkup_extended.py
│ ├── test_bind.py
│ ├── test_colorama.py
│ ├── test_configure.py
│ ├── test_contextualize.py
│ ├── test_coroutine_sink.py
│ ├── test_datetime.py
│ ├── test_deepcopy.py
│ ├── test_defaults.py
│ ├── test_exceptions_catch.py
│ ├── test_exceptions_formatting.py
│ ├── test_filesink_compression.py
│ ├── test_filesink_delay.py
│ ├── test_filesink_permissions.py
│ ├── test_filesink_retention.py
│ ├── test_filesink_rotation.py
│ ├── test_filesink_watch.py
│ ├── test_formatting.py
│ ├── test_get_frame.py
│ ├── test_interception.py
│ ├── test_levels.py
│ ├── test_locks.py
│ ├── test_multiprocessing.py
│ ├── test_opt.py
│ ├── test_parse.py
│ ├── test_patch.py
│ ├── test_pickling.py
│ ├── test_propagation.py
│ ├── test_recattr.py
│ ├── test_reinstall.py
│ ├── test_remove.py
│ ├── test_repr.py
│ ├── test_standard_handler.py
│ ├── test_threading.py
│ ├── test_type_hinting.py
│ └── typesafety/
│ └── test_logger.yml
└── tox.ini
================================================
FILE CONTENTS
================================================
================================================
FILE: .codecov.yml
================================================
comment: false
coverage:
status:
patch: no
changes: no
project:
default:
target: 100%
threshold:
================================================
FILE: .github/SECURITY.md
================================================
# Security Policy
## Supported Versions
As an open source product, only the latest major version will be patched for security vulnerabilities. Previous versions of Loguru will likely not be retroactively patched.
## Reporting a Vulnerability
To report a security issue, please email delgan.py@gmail.com with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue.
Once the vulnerability has been confirmed, it will be fixed as soon as possible if feasible. Alternatively, mitigating solutions will be detailed in the documentation: [Security considerations when using Loguru](https://loguru.readthedocs.io/en/stable/resources/recipes.html#security-considerations-when-using-loguru).
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: quarterly
labels: []
- package-ecosystem: pip
directory: /
schedule:
interval: quarterly
allow:
- dependency-type: direct
labels: []
ignore:
- dependency-name: '*'
update-types: [version-update:semver-patch]
- dependency-name: pytest-cov # Latest version needs a new "subprocess" configuration.
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
name: CodeQL
on:
push:
pull_request:
schedule:
- cron: 0 0 * * 0
jobs:
analyze:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-22.04
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [python]
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
================================================
FILE: .github/workflows/docs.yml
================================================
name: Docs
on: [push, pull_request]
jobs:
docs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Build documentation
run: |
tox -e docs
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on: [push, pull_request]
jobs:
lint:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Run linters
run: |
tox -e lint
================================================
FILE: .github/workflows/packaging.yml
================================================
name: Packaging
on: [push, pull_request]
jobs:
build:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Build package
run: |
tox -e build
- name: Upload package
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
publish:
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-22.04
needs: build
environment:
name: pypi
url: https://pypi.org/project/loguru/
permissions:
id-token: write
steps:
- name: Download package
uses: actions/download-artifact@v5
with:
name: python-package-distributions
path: dist/
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
on:
push:
pull_request:
schedule:
- cron: 0 0 * * 0
jobs:
tests:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
strategy:
fail-fast: false
matrix:
os:
- ubuntu-22.04
python-version:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- 3.14-dev
- pypy-3.11
allow-failure:
- false
include:
- os: ubuntu-22.04
python-version: '3.5'
allow-failure: false
container: python:3.5-slim
- os: ubuntu-22.04
python-version: '3.6'
allow-failure: false
container: python:3.6-slim
- os: windows-2022
python-version: '3.12'
allow-failure: false
- os: macos-15
python-version: '3.12'
allow-failure: false
runs-on: ${{ matrix.os }}
# Some versions of Python are not longer supported by GitHub Actions.
# For those, we use a container image and skip the setup-python step.
# It will be empty and ignored for the other matrix entries.
container: ${{ matrix.container }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Python
if: ${{ matrix.container == null }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Run tests
run: |
tox -e tests
continue-on-error: ${{ matrix.allow-failure }}
- name: Upload coverage data
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.os }}
path: coverage.xml
continue-on-error: ${{ matrix.allow-failure }}
push-coverage-to-codecov:
runs-on: ubuntu-22.04
needs: tests
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Download artifacts
uses: actions/download-artifact@v5
- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
fail_ci_if_error: true
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# Idea IDE
.idea/
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: trailing-whitespace
- id: check-added-large-files
- id: mixed-line-ending
args: [--fix=lf]
- repo: https://github.com/crate-ci/typos
rev: v1.35.3
hooks:
- id: typos
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.15.0
hooks:
- id: pretty-format-ini
args: [--autofix]
- id: pretty-format-yaml
args: [--autofix, --indent, '2']
- repo: https://github.com/ComPWA/taplo-pre-commit
rev: v0.9.3
hooks:
- id: taplo-format
- repo: https://github.com/ambv/black
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.8
hooks:
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
================================================
FILE: .readthedocs.yml
================================================
version: 2
build:
os: ubuntu-20.04
tools:
python: '3.11'
python:
install:
- method: pip
path: .
extra_requirements:
- dev
sphinx:
builder: html
configuration: docs/conf.py
formats:
- htmlzip
- pdf
- epub
================================================
FILE: .vscode/settings.json
================================================
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
================================================
FILE: CHANGELOG.rst
================================================
`Unreleased`_
=============
- Add new ``logger.reinstall()`` method to automatically set up the ``logger`` in spawned child processes (`#818 <https://github.com/Delgan/loguru/issues/818>`_, thanks `@monchin <https://github.com/monchin>`_).
- Fix incorrect microsecond value when formatting the log timestamp using ``{time:x}`` (`#1440 <https://github.com/Delgan/loguru/issues/1440>`_).
- Fix hex color short code expansion (`#1426 <https://github.com/Delgan/loguru/issues/1426>`_, thanks `@turkoid <https://github.com/turkoid>`_).
- Fix possible internal error when dealing with (rare) frame objects missing a ``f_lineno`` value (`#1435 <https://github.com/Delgan/loguru/issues/1435>`).
- Fix a regression preventing formatting of ``record["time"]`` when using ``zoneinfo.ZoneInfo`` timezones (`#1260 <https://github.com/Delgan/loguru/pull/1260>`_, thanks `@bijlpieter <https://github.com/bijlpieter>`_).
- Fix possible ``ValueError`` raised on Windows when system clock was set far ahead in the future (`#1291 <https://github.com/Delgan/loguru/issues/1291>`_).
- Respect the ``.level`` attribute of standard logging ``Handler`` used as sink, which was previously ignored (`#1409 <https://github.com/Delgan/loguru/issues/1409>`_).
- Allow the ``rotation`` argument of file sinks to accept a list of rotation conditions, any of which can trigger the rotation (`#1174 <https://github.com/Delgan/loguru/issues/1174>`_, thanks `@CollinHeist <https://github.com/CollinHeist>`_).
- Update the default log format to include the timezone offset since it produces less ambiguous logs (`#856 <https://github.com/Delgan/loguru/pull/856>`_, thanks `@tim-x-y-z <https://github.com/tim-x-y-z>`_).
- Honor the ``NO_COLOR`` environment variable which disables color output by default if ``colorize`` is not provided (`#1178 <https://github.com/Delgan/loguru/issues/1178>`_).
- Honor the ``FORCE_COLOR`` environment variable which enables color output by default if ``colorize`` is not provided (`#1305 <https://github.com/Delgan/loguru/issues/1305>`_, thanks `@nhurden <https://github.com/nhurden>`_).
- Add requirement for ``TERM`` environment variable not to be ``"dumb"`` to enable colorization (`#1287 <https://github.com/Delgan/loguru/pull/1287>`_, thanks `@snosov1 <https://github.com/snosov1>`_).
- Make ``logger.catch()`` usable as an asynchronous context manager (`#1084 <https://github.com/Delgan/loguru/issues/1084>`_).
- Make ``logger.catch()`` compatible with asynchronous generators (`#1302 <https://github.com/Delgan/loguru/issues/1302>`_).
`0.7.3`_ (2024-12-06)
=====================
- Fix Cython incompatibility caused by the absence of underlying stack frames, which resulted in a ``ValueError`` during logging (`#88 <https://github.com/Delgan/loguru/issues/88>`_).
- Fix possible ``RuntimeError`` when removing all handlers with ``logger.remove()`` due to thread-safety issue (`#1183 <https://github.com/Delgan/loguru/issues/1183>`_, thanks `@jeremyk <https://github.com/jeremyk>`_).
- Fix ``diagnose=True`` option of exception formatting not working as expected with Python 3.13 (`#1235 <https://github.com/Delgan/loguru/issues/1235>`_, thanks `@etianen <https://github.com/etianen>`_).
- Fix non-standard level names not fully compatible with ``logging.Formatter()`` (`#1231 <https://github.com/Delgan/loguru/issues/1231>`_, thanks `@yechielb2000 <https://github.com/yechielb2000>`_).
- Fix inability to display a literal ``"\"`` immediately before color markups (`#988 <https://github.com/Delgan/loguru/issues/988>`_).
- Fix possible infinite recursion when an exception is raised from a ``__repr__`` method decorated with ``logger.catch()`` (`#1044 <https://github.com/Delgan/loguru/issues/1044>`_).
- Improve performance of ``datetime`` formatting while logging messages (`#1201 <https://github.com/Delgan/loguru/issues/1201>`_, thanks `@trim21 <https://github.com/trim21>`_).
- Reduce startup time in the presence of installed but unused ``IPython`` third-party library (`#1001 <https://github.com/Delgan/loguru/issues/1001>`_, thanks `@zakstucke <https://github.com/zakstucke>`_).
`0.7.2`_ (2023-09-11)
=====================
- Add support for formatting of ``ExceptionGroup`` errors (`#805 <https://github.com/Delgan/loguru/issues/805>`_).
- Fix possible ``RuntimeError`` when using ``multiprocessing.set_start_method()`` after importing the ``logger`` (`#974 <https://github.com/Delgan/loguru/issues/974>`_).
- Fix formatting of possible ``__notes__`` attached to an ``Exception`` (`#980 <https://github.com/Delgan/loguru/issues/980>`_).
`0.7.1`_ (2023-09-04)
=====================
- Add a new ``context`` optional argument to ``logger.add()`` specifying ``multiprocessing`` context (like ``"spawn"`` or ``"fork"``) to be used internally instead of the default one (`#851 <https://github.com/Delgan/loguru/issues/851>`_).
- Add support for true colors on Windows using ANSI/VT console when available (`#934 <https://github.com/Delgan/loguru/issues/934>`_, thanks `@tunaflsh <https://github.com/tunaflsh>`_).
- Fix possible deadlock when calling ``logger.complete()`` with concurrent logging of an asynchronous sink (`#906 <https://github.com/Delgan/loguru/issues/906>`_).
- Fix file possibly rotating too early or too late when re-starting an application around midnight (`#894 <https://github.com/Delgan/loguru/issues/894>`_).
- Fix inverted ``"<hide>"`` and ``"<strike>"`` color tags (`#943 <https://github.com/Delgan/loguru/pull/943>`_, thanks `@tunaflsh <https://github.com/tunaflsh>`_).
- Fix possible untraceable errors raised when logging non-unpicklable ``Exception`` instances while using ``enqueue=True`` (`#329 <https://github.com/Delgan/loguru/issues/329>`_).
- Fix possible errors raised when logging non-picklable ``Exception`` instances while using ``enqueue=True`` (`#342 <https://github.com/Delgan/loguru/issues/342>`_, thanks `@ncoudene <https://github.com/ncoudene>`_).
- Fix missing seconds and microseconds when formatting timezone offset that requires such accuracy (`#961 <https://github.com/Delgan/loguru/issues/961>`_).
- Raise ``ValueError`` if an attempt to use nanosecond precision for time formatting is detected (`#855 <https://github.com/Delgan/loguru/issues/855>`_).
`0.7.0`_ (2023-04-10)
=====================
- Update ``InterceptHandler`` recipe to make it compatible with Python 3.11 (`#654 <https://github.com/Delgan/loguru/issues/654>`_).
- Add a new ``watch`` optional argument to file sinks in order to automatically re-create possibly deleted or changed file (`#471 <https://github.com/Delgan/loguru/issues/471>`_).
- Make ``patch()`` calls cumulative instead of overriding the possibly existing patching function (`#462 <https://github.com/Delgan/loguru/issues/462>`_).
- Make sinks added with ``enqueue=True`` and ``catch=False`` still process logged messages in case of internal exception (`#833 <https://github.com/Delgan/loguru/issues/833>`_).
- Avoid possible deadlocks caused by re-using the logger inside a sink, a signal handler or a ``__del__`` method. Since the logger is not re-entrant, such misuse will be detected and will now generate a ``RuntimeError`` (`#712 <https://github.com/Delgan/loguru/issues/712>`_, thanks `@jacksmith15 <https://github.com/jacksmith15>`_).
- Fix file sink rotation using an aware ``datetime.time`` for which the timezone was ignored (`#697 <https://github.com/Delgan/loguru/issues/697>`_).
- Fix logs colorization not automatically enabled for Jupyter Notebook and Google Colab (`#494 <https://github.com/Delgan/loguru/issues/494>`_).
- Fix logs colorization not automatically enabled for Github Actions and others CI platforms (`#604 <https://github.com/Delgan/loguru/issues/604>`_).
- Fix ``logger.complete()`` possibly hanging forever when ``enqueue=True`` and ``catch=False`` if internal thread killed due to ``Exception`` raised by sink (`#647 <https://github.com/Delgan/loguru/issues/647>`_).
- Fix incompatibility with ``freezegun`` library used to simulate time (`#600 <https://github.com/Delgan/loguru/issues/600>`_).
- Raise exception if ``logger.catch()`` is used to wrap a class instead of a function to avoid unexpected behavior (`#623 <https://github.com/Delgan/loguru/issues/623>`_).
`0.6.0`_ (2022-01-29)
=====================
- Remove internal use of ``pickle.loads()`` to fix the (finally rejected) security vulnerability referenced as `CVE-2022-0329 <https://nvd.nist.gov/vuln/detail/CVE-2022-0329>`_ (`#563 <https://github.com/Delgan/loguru/issues/563>`_).
- Modify coroutine sink to make it discard log messages when ``loop=None`` and no event loop is running (due to internally using ``asyncio.get_running_loop()`` in place of ``asyncio.get_event_loop()``).
- Remove the possibility to add a coroutine sink with ``enqueue=True`` if ``loop=None`` and no event loop is running.
- Change default encoding of file sink to be ``utf8`` instead of ``locale.getpreferredencoding()`` (`#339 <https://github.com/Delgan/loguru/issues/339>`_).
- Prevent non-ascii characters to be escaped while logging JSON message with ``serialize=True`` (`#575 <https://github.com/Delgan/loguru/pull/575>`_, thanks `@ponponon <https://github.com/ponponon>`_).
- Fix ``flake8`` errors and improve code readability (`#353 <https://github.com/Delgan/loguru/issues/353>`_, thanks `@AndrewYakimets <https://github.com/AndrewYakimets>`_).
`0.5.3`_ (2020-09-20)
=====================
- Fix child process possibly hanging at exit while combining ``enqueue=True`` with third party library like ``uwsgi`` (`#309 <https://github.com/Delgan/loguru/issues/309>`_, thanks `@dstlmrk <https://github.com/dstlmrk>`_).
- Fix possible exception during formatting of non-string messages (`#331 <https://github.com/Delgan/loguru/issues/331>`_).
`0.5.2`_ (2020-09-06)
=====================
- Fix ``AttributeError`` within handlers using ``serialize=True`` when calling ``logger.exception()`` outside of the context of an exception (`#296 <https://github.com/Delgan/loguru/issues/296>`_).
- Fix error while logging an exception containing a non-picklable ``value`` to a handler with ``enqueue=True`` (`#298 <https://github.com/Delgan/loguru/issues/298>`_).
- Add support for async callable classes (with ``__call__`` method) used as sinks (`#294 <https://github.com/Delgan/loguru/pull/294>`_, thanks `@jessekrubin <https://github.com/jessekrubin>`_).
`0.5.1`_ (2020-06-12)
=====================
- Modify the way the ``extra`` dict is used by ``LogRecord`` in order to prevent possible ``KeyError`` with standard ``logging`` handlers (`#271 <https://github.com/Delgan/loguru/issues/271>`_).
- Add a new ``default`` optional argument to ``logger.catch()``, it should be the returned value by the decorated function in case an error occurred (`#272 <https://github.com/Delgan/loguru/issues/272>`_).
- Fix ``ValueError`` when using ``serialize=True`` in combination with ``logger.catch()`` or ``logger.opt(record=True)`` due to circular reference of the ``record`` dict (`#286 <https://github.com/Delgan/loguru/issues/286>`_).
`0.5.0`_ (2020-05-17)
=====================
- Remove the possibility to modify the severity ``no`` of levels once they have been added in order to prevent surprising behavior (`#209 <https://github.com/Delgan/loguru/issues/209>`_).
- Add better support for "structured logging" by automatically adding ``**kwargs`` to the ``extra`` dict besides using these arguments to format the message. This behavior can be disabled by setting the new ``.opt(capture=False)`` parameter (`#2 <https://github.com/Delgan/loguru/issues/2>`_).
- Add a new ``onerror`` optional argument to ``logger.catch()``, it should be a function which will be called when an exception occurs in order to customize error handling (`#224 <https://github.com/Delgan/loguru/issues/224>`_).
- Add a new ``exclude`` optional argument to ``logger.catch()``, is should be a type of exception to be purposefully ignored and propagated to the caller without being logged (`#248 <https://github.com/Delgan/loguru/issues/248>`_).
- Modify ``complete()`` to make it callable from non-asynchronous functions, it can thus be used if ``enqueue=True`` to make sure all messages have been processed (`#231 <https://github.com/Delgan/loguru/issues/231>`_).
- Fix possible deadlocks on Linux when ``multiprocessing.Process()`` collides with ``enqueue=True`` or ``threading`` (`#231 <https://github.com/Delgan/loguru/issues/231>`_).
- Fix ``compression`` function not executable concurrently due to file renaming (to resolve conflicts) being performed after and not before it (`#243 <https://github.com/Delgan/loguru/issues/243>`_).
- Fix the filter function listing files for ``retention`` being too restrictive, it now matches files based on the pattern ``"root(.*).ext(.*)"`` (`#229 <https://github.com/Delgan/loguru/issues/229>`_).
- Fix the impossibility to ``remove()`` a handler if an exception is raised while the sink' ``stop()`` function is called (`#237 <https://github.com/Delgan/loguru/issues/237>`_).
- Fix file sink left in an unstable state if an exception occurred during ``retention`` or ``compression`` process (`#238 <https://github.com/Delgan/loguru/issues/238>`_).
- Fix situation where changes made to ``record["message"]`` were unexpectedly ignored when ``opt(colors=True)``, causing "out-of-date" ``message`` to be logged due to implementation details (`#221 <https://github.com/Delgan/loguru/issues/221>`_).
- Fix possible exception if a stream having an ``isatty()`` method returning ``True`` but not being compatible with ``colorama`` is used on Windows (`#249 <https://github.com/Delgan/loguru/issues/249>`_).
- Fix exceptions occurring in coroutine sinks never retrieved and hence causing warnings (`#227 <https://github.com/Delgan/loguru/issues/227>`_).
`0.4.1`_ (2020-01-19)
=====================
- Deprecate the ``ansi`` parameter of ``.opt()`` in favor of ``colors`` which is a name more appropriate.
- Prevent unrelated files and directories to be incorrectly collected thus causing errors during the ``retention`` process (`#195 <https://github.com/Delgan/loguru/issues/195>`_, thanks `@gazpachoking <https://github.com/gazpachoking>`_).
- Strip color markups contained in ``record["message"]`` when logging with ``.opt(ansi=True)`` instead of leaving them as is (`#198 <https://github.com/Delgan/loguru/issues/198>`_).
- Ignore color markups contained in ``*args`` and ``**kwargs`` when logging with ``.opt(ansi=True)``, leave them as is instead of trying to use them to colorize the message which could cause undesirable errors (`#197 <https://github.com/Delgan/loguru/issues/197>`_).
`0.4.0`_ (2019-12-02)
=====================
- Add support for coroutine functions used as sinks and add the new ``logger.complete()`` asynchronous method to ``await`` them (`#171 <https://github.com/Delgan/loguru/issues/171>`_).
- Add a way to filter logs using one level per module in the form of a ``dict`` passed to the ``filter`` argument (`#148 <https://github.com/Delgan/loguru/issues/148>`_).
- Add type hints to annotate the public methods using a ``.pyi`` stub file (`#162 <https://github.com/Delgan/loguru/issues/162>`_).
- Add support for ``copy.deepcopy()`` of the ``logger`` allowing multiple independent loggers with separate set of handlers (`#72 <https://github.com/Delgan/loguru/issues/72>`_).
- Add the possibility to convert ``datetime`` to UTC before formatting (in logs and filenames) by adding ``"!UTC"`` at the end of the time format specifier (`#128 <https://github.com/Delgan/loguru/issues/128>`_).
- Add the level ``name`` as the first argument of namedtuple returned by the ``.level()`` method.
- Remove ``class`` objects from the list of supported sinks and restrict usage of ``**kwargs`` in ``.add()`` to file sink only. User is in charge of instantiating sink and wrapping additional keyword arguments if needed, before passing it to the ``.add()`` method.
- Rename the ``logger.configure()`` keyword argument ``patch`` to ``patcher`` so it better matches the signature of ``logger.patch()``.
- Fix incompatibility with ``multiprocessing`` on Windows by entirely refactoring the internal structure of the ``logger`` so it can be inherited by child processes along with added handlers (`#108 <https://github.com/Delgan/loguru/issues/108>`_).
- Fix ``AttributeError`` while using a file sink on some distributions (like Alpine Linux) missing the ``os.getxattr`` and ``os.setxattr`` functions (`#158 <https://github.com/Delgan/loguru/pull/158>`_, thanks `@joshgordon <https://github.com/joshgordon>`_).
- Fix values wrongly displayed for keyword arguments during exception formatting with ``diagnose=True`` (`#144 <https://github.com/Delgan/loguru/issues/144>`_).
- Fix logging messages wrongly chopped off at the end while using standard ``logging.Handler`` sinks with ``.opt(raw=True)`` (`#136 <https://github.com/Delgan/loguru/issues/136>`_).
- Fix potential errors during rotation if destination file exists due to large resolution clock on Windows (`#179 <https://github.com/Delgan/loguru/issues/179>`_).
- Fix an error using a ``filter`` function "by name" while receiving a log with ``record["name"]`` equals to ``None``.
- Fix incorrect record displayed while handling errors (if ``catch=True``) occurring because of non-picklable objects (if ``enqueue=True``).
- Prevent hypothetical ``ImportError`` if a Python installation is missing the built-in ``distutils`` module (`#118 <https://github.com/Delgan/loguru/issues/118>`_).
- Raise ``TypeError`` instead of ``ValueError`` when a ``logger`` method is called with argument of invalid type.
- Raise ``ValueError`` if the built-in ``format()`` and ``filter()`` functions are respectively used as ``format`` and ``filter`` arguments of the ``add()`` method. This helps the user to understand the problem, as such a mistake can quite easily occur (`#177 <https://github.com/Delgan/loguru/issues/177>`_).
- Remove inheritance of some record dict attributes to ``str`` (for ``"level"``, ``"file"``, ``"thread"`` and ``"process"``).
- Give a name to the worker thread used when ``enqueue=True`` (`#174 <https://github.com/Delgan/loguru/pull/174>`_, thanks `@t-mart <https://github.com/t-mart>`_).
`0.3.2`_ (2019-07-21)
=====================
- Fix exception during import when executing Python with ``-s`` and ``-S`` flags causing ``site.USER_SITE`` to be missing (`#114 <https://github.com/Delgan/loguru/issues/114>`_).
`0.3.1`_ (2019-07-13)
=====================
- Fix ``retention`` and ``rotation`` issues when file sink initialized with ``delay=True`` (`#113 <https://github.com/Delgan/loguru/issues/113>`_).
- Fix ``"sec"`` no longer recognized as a valid duration unit for file ``rotation`` and ``retention`` arguments.
- Ensure stack from the caller is displayed while formatting exception of a function decorated with ``@logger.catch`` when ``backtrace=False``.
- Modify datetime used to automatically rename conflicting file when rotating (it happens if file already exists because ``"{time}"`` not presents in filename) so it's based on the file creation time rather than the current time.
`0.3.0`_ (2019-06-29)
=====================
- Remove all dependencies previously needed by ``loguru`` (on Windows platform, it solely remains ``colorama`` and ``win32-setctime``).
- Add a new ``logger.patch()`` method which can be used to modify the record dict on-the-fly before it's being sent to the handlers.
- Modify behavior of sink option ``backtrace`` so it only extends the stacktrace upward, the display of variables values is now controlled with the new ``diagnose`` argument (`#49 <https://github.com/Delgan/loguru/issues/49>`_).
- Change behavior of ``rotation`` option in file sinks: it is now based on the file creation time rather than the current time, note that proper support may differ depending on your platform (`#58 <https://github.com/Delgan/loguru/issues/58>`_).
- Raise errors on unknowns color tags rather than silently ignoring them (`#57 <https://github.com/Delgan/loguru/issues/57>`_).
- Add the possibility to auto-close color tags by using ``</>`` (e.g. ``<yellow>message</>``).
- Add coloration of exception traceback even if ``diagnose`` and ``backtrace`` options are ``False``.
- Add a way to limit the depth of formatted exceptions traceback by setting the conventional ``sys.tracebacklimit`` variable (`#77 <https://github.com/Delgan/loguru/issues/77>`_).
- Add ``__repr__`` value to the ``logger`` for convenient debugging (`#84 <https://github.com/Delgan/loguru/issues/84>`_).
- Remove colors tags mixing directives (e.g. ``<red,blue>``) for simplification.
- Make the ``record["exception"]`` attribute unpackable as a ``(type, value, traceback)`` tuple.
- Fix error happening in some rare circumstances because ``frame.f_globals`` dict did not contain ``"__name__"`` key and hence prevented Loguru to retrieve the module's name. From now, ``record["name"]`` will be equal to ``None`` in such case (`#62 <https://github.com/Delgan/loguru/issues/62>`_).
- Fix logging methods not being serializable with ``pickle`` and hence raising exception while being passed to some ``multiprocessing`` functions (`#102 <https://github.com/Delgan/loguru/issues/102>`_).
- Fix exception stack trace not colorizing source code lines on Windows.
- Fix possible ``AttributeError`` while formatting exceptions within a ``celery`` task (`#52 <https://github.com/Delgan/loguru/issues/52>`_).
- Fix ``logger.catch`` decorator not working with generator and coroutine functions (`#75 <https://github.com/Delgan/loguru/issues/75>`_).
- Fix ``record["path"]`` case being normalized for no necessary reason (`#85 <https://github.com/Delgan/loguru/issues/85>`_).
- Fix some Windows terminal emulators (mintty) not correctly detected as supporting colors, causing ansi codes to be automatically stripped (`#104 <https://github.com/Delgan/loguru/issues/104>`_).
- Fix handler added with ``enqueue=True`` stopping working if exception was raised in sink although ``catch=True``.
- Fix thread-safety of ``enable()`` and ``disable()`` being called during logging.
- Use Tox to run tests (`#41 <https://github.com/Delgan/loguru/issues/41>`_).
`0.2.5`_ (2019-01-20)
=====================
- Modify behavior of sink option ``backtrace=False`` so it doesn't extend traceback upward automatically (`#30 <https://github.com/Delgan/loguru/issues/30>`_).
- Fix import error on some platforms using Python 3.5 with limited ``localtime()`` support (`#33 <https://github.com/Delgan/loguru/issues/33>`_).
- Fix incorrect time formatting of locale month using ``MMM`` and ``MMMM`` tokens (`#34 <https://github.com/Delgan/loguru/pull/34>`_, thanks `@nasyxx <https://github.com/nasyxx>`_).
- Fix race condition permitting writing on a stopped handler.
`0.2.4`_ (2018-12-26)
=====================
- Fix adding handler while logging which was not thread-safe (`#22 <https://github.com/Delgan/loguru/issues/22>`_).
`0.2.3`_ (2018-12-16)
=====================
- Add support for PyPy.
- Add support for Python 3.5.
- Fix incompatibility with ``awscli`` by downgrading required ``colorama`` dependency version (`#12 <https://github.com/Delgan/loguru/issues/12>`_).
`0.2.2`_ (2018-12-12)
=====================
- Deprecate ``logger.start()`` and ``logger.stop()`` methods in favor of ``logger.add()`` and ``logger.remove()`` (`#3 <https://github.com/Delgan/loguru/issues/3>`_).
- Fix ignored formatting while using ``logging.Handler`` sinks (`#4 <https://github.com/Delgan/loguru/issues/4>`_).
- Fix impossibility to set empty environment variable color on Windows (`#7 <https://github.com/Delgan/loguru/issues/7>`_).
`0.2.1`_ (2018-12-08)
=====================
- Fix typo preventing README to be correctly displayed on PyPI.
`0.2.0`_ (2018-12-08)
=====================
- Remove the ``parser`` and refactor it into the ``logger.parse()`` method.
- Remove the ``notifier`` and its dependencies (``pip install notifiers`` should be used instead).
`0.1.0`_ (2018-12-07)
=====================
- Add logger.
- Add notifier.
- Add parser.
`0.0.1`_ (2017-09-04)
=====================
Initial release.
.. _Unreleased: https://github.com/delgan/loguru/compare/0.7.3...master
.. _0.7.3: https://github.com/delgan/loguru/releases/tag/0.7.3
.. _0.7.2: https://github.com/delgan/loguru/releases/tag/0.7.2
.. _0.7.1: https://github.com/delgan/loguru/releases/tag/0.7.1
.. _0.7.0: https://github.com/delgan/loguru/releases/tag/0.7.0
.. _0.6.0: https://github.com/delgan/loguru/releases/tag/0.6.0
.. _0.5.3: https://github.com/delgan/loguru/releases/tag/0.5.3
.. _0.5.2: https://github.com/delgan/loguru/releases/tag/0.5.2
.. _0.5.1: https://github.com/delgan/loguru/releases/tag/0.5.1
.. _0.5.0: https://github.com/delgan/loguru/releases/tag/0.5.0
.. _0.4.1: https://github.com/delgan/loguru/releases/tag/0.4.1
.. _0.4.0: https://github.com/delgan/loguru/releases/tag/0.4.0
.. _0.3.2: https://github.com/delgan/loguru/releases/tag/0.3.2
.. _0.3.1: https://github.com/delgan/loguru/releases/tag/0.3.1
.. _0.3.0: https://github.com/delgan/loguru/releases/tag/0.3.0
.. _0.2.5: https://github.com/delgan/loguru/releases/tag/0.2.5
.. _0.2.4: https://github.com/delgan/loguru/releases/tag/0.2.4
.. _0.2.3: https://github.com/delgan/loguru/releases/tag/0.2.3
.. _0.2.2: https://github.com/delgan/loguru/releases/tag/0.2.2
.. _0.2.1: https://github.com/delgan/loguru/releases/tag/0.2.1
.. _0.2.0: https://github.com/delgan/loguru/releases/tag/0.2.0
.. _0.1.0: https://github.com/delgan/loguru/releases/tag/0.1.0
.. _0.0.1: https://github.com/delgan/loguru/releases/tag/0.0.1
================================================
FILE: CONTRIBUTING.rst
================================================
Thank you for considering improving `Loguru`, any contribution is much welcome!
.. _minimal reproducible example: https://stackoverflow.com/help/mcve
.. _open a new issue: https://github.com/Delgan/loguru/issues/new
.. _open a pull request: https://github.com/Delgan/loguru/compare
.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
.. _Loguru: https://github.com/Delgan/loguru
Automated Contributions Policy
------------------------------
Recent advances in artificial intelligence (AI) offer powerful tools to identify bugs and accelerate development, but their usage must remain carefully controlled.
The use of AI tools to interact with this project is welcome under the following principles:
- Any content generated or significantly refined by AI must be disclosed as such at the time of submission.
- While AI can help with drafting your PRs or issues, describing problems or questions in your own words is strongly preferred.
- Always review and test the output generated by AI tools. Do not submit AI output that you do not understand or cannot verify.
- Fully-automated contributions without human oversight, such as autonomous agents, are strictly prohibited.
PRs and issues may be closed without proper review if they do not adhere to these guidelines.
Asking questions
----------------
If you have any question about `Loguru`, if you are seeking for help, or if you would like to suggest a new feature, you are encouraged to `open a new issue`_ so we can discuss it. Bringing new ideas and pointing out elements needing clarification allows to make this library always better!
Reporting a bug
---------------
If you encountered an unexpected behavior using `Loguru`, please `open a new issue`_ and describe the problem you have spotted. Be as specific as possible in the description of the trouble so we can easily analyse it and quickly fix it.
An ideal bug report includes:
* The Python version you are using
* The `Loguru` version you are using (you can find it with ``print(loguru.__version__)``)
* Your operating system name and version (Linux, MacOS, Windows)
* Your development environment and local setup (IDE, Terminal, project context, any relevant information that could be useful)
* Some `minimal reproducible example`_
Implementing changes
--------------------
If you are willing to enhance `Loguru` by implementing non-trivial changes, please `open a new issue`_ first to keep a reference about why such modifications are made (and potentially avoid unneeded work).
Prefer using a relatively recent Python version as some dependencies required for development may have dropped support for oldest Python versions. Then, the workflow would look as follows:
1. Fork the `Loguru`_ project from GitHub.
2. Clone the repository locally::
$ git clone git@github.com:your_name_here/loguru.git
$ cd loguru
3. Activate your virtual environment::
$ python -m venv env
$ source env/bin/activate
4. Install `Loguru` in development mode::
$ pip install -e ".[dev]"
5. Install pre-commit hooks that will check your commits::
$ pre-commit install --install-hooks
6. Create a new branch from ``master``::
$ git checkout master
$ git branch fix_bug
$ git checkout fix_bug
7. Implement the modifications wished. During the process of development, honor `PEP 8`_ as much as possible.
8. Add unit tests (don't hesitate to be exhaustive!) and ensure none are failing using::
$ tox -e tests
9. Remember to update documentation if required.
10. If your development modifies `Loguru` behavior, update the ``CHANGELOG.rst`` file with what you improved.
11. ``add`` and ``commit`` your changes, then ``push`` your local project::
$ git add .
$ git commit -m 'Add succinct explanation of what changed'
$ git push origin fix_bug
12. If previous step failed due to the pre-commit hooks, fix reported errors and try again.
13. Finally, `open a pull request`_ before getting it merged!
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<a href="#readme">
<img alt="Loguru logo" src="https://raw.githubusercontent.com/Delgan/loguru/master/docs/_static/img/logo.png">
<!-- Logo credits: Sambeet from Pixabay -->
<!-- Logo fonts: Comfortaa + Raleway -->
</a>
</p>
<p align="center">
<a href="https://pypi.python.org/pypi/loguru"><img alt="Pypi version" src="https://img.shields.io/pypi/v/loguru.svg"></a>
<a href="https://pypi.python.org/pypi/loguru"><img alt="Python versions" src="https://img.shields.io/badge/python-3.5%2B%20%7C%20PyPy-blue.svg"></a>
<a href="https://loguru.readthedocs.io/en/stable/index.html"><img alt="Documentation" src="https://img.shields.io/readthedocs/loguru.svg"></a>
<a href="https://github.com/Delgan/loguru/actions/workflows/tests.yml?query=branch:master"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/Delgan/loguru/tests.yml?branch=master"></a>
<a href="https://codecov.io/gh/delgan/loguru/branch/master"><img alt="Coverage" src="https://img.shields.io/codecov/c/github/delgan/loguru/master.svg"></a>
<a href="https://app.codacy.com/gh/Delgan/loguru/dashboard"><img alt="Code quality" src="https://img.shields.io/codacy/grade/be7337df3c0d40d1929eb7f79b1671a6.svg"></a>
<a href="https://github.com/Delgan/loguru/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/github/license/delgan/loguru.svg"></a>
</p>
<p align="center">
<a href="#readme">
<img alt="Loguru logo" src="https://raw.githubusercontent.com/Delgan/loguru/master/docs/_static/img/demo.gif">
</a>
</p>
______________________________________________________________________
**Loguru** is a library which aims to bring enjoyable logging in Python.
Did you ever feel lazy about configuring a logger and used `print()` instead?... I did, yet logging is fundamental to every application and eases the process of debugging. Using **Loguru** you have no excuse not to use logging from the start, this is as simple as `from loguru import logger`.
Also, this library is intended to make Python logging less painful by adding a bunch of useful functionalities that solve caveats of the standard loggers. Using logs in your application should be an automatism, **Loguru** tries to make it both pleasant and powerful.
<!-- end-of-readme-intro -->
## Installation
```
pip install loguru
```
## Features
- [Ready to use out of the box without boilerplate](#ready-to-use-out-of-the-box-without-boilerplate)
- [No Handler, no Formatter, no Filter: one function to rule them all](#no-handler-no-formatter-no-filter-one-function-to-rule-them-all)
- [Easier file logging with rotation / retention / compression](#easier-file-logging-with-rotation--retention--compression)
- [Modern string formatting using braces style](#modern-string-formatting-using-braces-style)
- [Exceptions catching within threads or main](#exceptions-catching-within-threads-or-main)
- [Pretty logging with colors](#pretty-logging-with-colors)
- [Asynchronous, Thread-safe, Multiprocess-safe](#asynchronous-thread-safe-multiprocess-safe)
- [Fully descriptive exceptions](#fully-descriptive-exceptions)
- [Structured logging as needed](#structured-logging-as-needed)
- [Lazy evaluation of expensive functions](#lazy-evaluation-of-expensive-functions)
- [Customizable levels](#customizable-levels)
- [Better datetime handling](#better-datetime-handling)
- [Suitable for scripts and libraries](#suitable-for-scripts-and-libraries)
- [Entirely compatible with standard logging](#entirely-compatible-with-standard-logging)
- [Personalizable defaults through environment variables](#personalizable-defaults-through-environment-variables)
- [Convenient parser](#convenient-parser)
- [Exhaustive notifier](#exhaustive-notifier)
- <s>[10x faster than built-in logging](#10x-faster-than-built-in-logging)</s>
## Take the tour
### Ready to use out of the box without boilerplate
The main concept of Loguru is that **there is one and only one** [`logger`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger).
For convenience, it is pre-configured and outputs to `stderr` to begin with (but that's entirely configurable).
```python
from loguru import logger
logger.debug("That's it, beautiful and simple logging!")
```
The [`logger`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger) is just an interface which dispatches log messages to configured handlers. Simple, right?
### No Handler, no Formatter, no Filter: one function to rule them all
How to add a handler? How to set up logs formatting? How to filter messages? How to set level?
One answer: the [`add()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add) function.
```python
logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO")
```
This function should be used to register [sinks](https://loguru.readthedocs.io/en/stable/api/logger.html#sink) which are responsible for managing [log messages](https://loguru.readthedocs.io/en/stable/api/logger.html#message) contextualized with a [record dict](https://loguru.readthedocs.io/en/stable/api/logger.html#record). A sink can take many forms: a simple function, a string path, a file-like object, a coroutine function or a built-in Handler.
Note that you may also [`remove()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.remove) a previously added handler by using the identifier returned while adding it. This is particularly useful if you want to supersede the default `stderr` handler: just call `logger.remove()` to make a fresh start.
### Easier file logging with rotation / retention / compression
If you want to send logged messages to a file, you just have to use a string path as the sink. It can be automatically timed too for convenience:
```python
logger.add("file_{time}.log")
```
It is also [easily configurable](https://loguru.readthedocs.io/en/stable/api/logger.html#file) if you need rotating logger, if you want to remove older logs, or if you wish to compress your files at closure.
```python
logger.add("file_1.log", rotation="500 MB") # Automatically rotate too big file
logger.add("file_2.log", rotation="12:00") # New file is created each day at noon
logger.add("file_3.log", rotation="1 week") # Once the file is too old, it's rotated
logger.add("file_X.log", retention="10 days") # Cleanup after some time
logger.add("file_Y.log", compression="zip") # Save some loved space
```
### Modern string formatting using braces style
Loguru favors the much more elegant and powerful `{}` formatting over `%`, logging functions are actually equivalent to `str.format()`.
```python
logger.info("If you're using Python {}, prefer {feature} of course!", 3.6, feature="f-strings")
```
### Exceptions catching within threads or main
Have you ever seen your program crashing unexpectedly without seeing anything in the log file? Did you ever notice that exceptions occurring in threads were not logged? This can be solved using the [`catch()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.catch) decorator / context manager which ensures that any error is correctly propagated to the [`logger`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger).
```python
@logger.catch
def my_function(x, y, z):
# An error? It's caught anyway!
return 1 / (x + y + z)
```
### Pretty logging with colors
Loguru automatically adds colors to your logs if your terminal is compatible. You can define your favorite style by using [markup tags](https://loguru.readthedocs.io/en/stable/api/logger.html#color) in the sink format.
```python
logger.add(sys.stdout, colorize=True, format="<green>{time}</green> <level>{message}</level>")
```
### Asynchronous, Thread-safe, Multiprocess-safe
All sinks added to the [`logger`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger) are thread-safe by default. They are not multiprocess-safe, but you can `enqueue` the messages to ensure logs integrity. This same argument can also be used if you want async logging.
```python
logger.add("somefile.log", enqueue=True)
```
Coroutine functions used as sinks are also supported and should be awaited with [`complete()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.complete).
### Fully descriptive exceptions
Logging exceptions that occur in your code is important to track bugs, but it's quite useless if you don't know why it failed. Loguru helps you identify problems by allowing the entire stack trace to be displayed, including values of variables (thanks [`better_exceptions`](https://github.com/Qix-/better-exceptions) for this!).
The code:
```python
# Caution, "diagnose=True" is the default and may leak sensitive data in prod
logger.add("out.log", backtrace=True, diagnose=True)
def func(a, b):
return a / b
def nested(c):
try:
func(5, c)
except ZeroDivisionError:
logger.exception("What?!")
nested(0)
```
Would result in:
```none
2018-07-17 01:38:43.975 | ERROR | __main__:nested:10 - What?!
Traceback (most recent call last):
File "test.py", line 12, in <module>
nested(0)
└ <function nested at 0x7f5c755322f0>
> File "test.py", line 8, in nested
func(5, c)
│ └ 0
└ <function func at 0x7f5c79fc2e18>
File "test.py", line 4, in func
return a / b
│ └ 0
└ 5
ZeroDivisionError: division by zero
```
Note that this feature won't work on default Python REPL due to unavailable frame data.
See also: [Security considerations when using Loguru](https://loguru.readthedocs.io/en/stable/resources/recipes.html#security-considerations-when-using-loguru).
### Structured logging as needed
Want your logs to be serialized for easier parsing or to pass them around? Using the `serialize` argument, each log message will be converted to a JSON string before being sent to the configured sink.
```python
logger.add(custom_sink_function, serialize=True)
```
Using [`bind()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.bind) you can contextualize your logger messages by modifying the `extra` record attribute.
```python
logger.add("file.log", format="{extra[ip]} {extra[user]} {message}")
context_logger = logger.bind(ip="192.168.0.1", user="someone")
context_logger.info("Contextualize your logger easily")
context_logger.bind(user="someone_else").info("Inline binding of extra attribute")
context_logger.info("Use kwargs to add context during formatting: {user}", user="anybody")
```
It is possible to modify a context-local state temporarily with [`contextualize()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.contextualize):
```python
with logger.contextualize(task=task_id):
do_something()
logger.info("End of task")
```
You can also have more fine-grained control over your logs by combining [`bind()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.bind) and `filter`:
```python
logger.add("special.log", filter=lambda record: "special" in record["extra"])
logger.debug("This message is not logged to the file")
logger.bind(special=True).info("This message, though, is logged to the file!")
```
Finally, the [`patch()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.patch) method allows dynamic values to be attached to the record dict of each new message:
```python
logger.add(sys.stderr, format="{extra[utc]} {message}")
logger = logger.patch(lambda record: record["extra"].update(utc=datetime.utcnow()))
```
### Lazy evaluation of expensive functions
Sometime you would like to log verbose information without performance penalty in production, you can use the [`opt()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.opt) method to achieve this.
```python
logger.opt(lazy=True).debug("If sink level <= DEBUG: {x}", x=lambda: expensive_function(2**64))
# By the way, "opt()" serves many usages
logger.opt(exception=True).info("Error stacktrace added to the log message (tuple accepted too)")
logger.opt(colors=True).info("Per message <blue>colors</blue>")
logger.opt(record=True).info("Display values from the record (eg. {record[thread]})")
logger.opt(raw=True).info("Bypass sink formatting\n")
logger.opt(depth=1).info("Use parent stack context (useful within wrapped functions)")
logger.opt(capture=False).info("Keyword arguments not added to {dest} dict", dest="extra")
```
### Customizable levels
Loguru comes with all standard [logging levels](https://loguru.readthedocs.io/en/stable/api/logger.html#levels) to which [`trace()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.trace) and [`success()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.success) are added. Do you need more? Then, just create it by using the [`level()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.level) function.
```python
new_level = logger.level("SNAKY", no=38, color="<yellow>", icon="🐍")
logger.log("SNAKY", "Here we go!")
```
### Better datetime handling
The standard logging is bloated with arguments like `datefmt` or `msecs`, `%(asctime)s` and `%(created)s`, naive datetimes without timezone information, not intuitive formatting, etc. Loguru [fixes it](https://loguru.readthedocs.io/en/stable/api/logger.html#time):
```python
logger.add("file.log", format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}")
```
### Suitable for scripts and libraries
Using the logger in your scripts is easy, and you can [`configure()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.configure) it at start. To use Loguru from inside a library, remember to never call [`add()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add) but use [`disable()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.disable) instead so logging functions become no-op. If a developer wishes to see your library's logs, they can [`enable()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.enable) it again.
```python
# For scripts
config = {
"handlers": [
{"sink": sys.stdout, "format": "{time} - {message}"},
{"sink": "file.log", "serialize": True},
],
"extra": {"user": "someone"}
}
logger.configure(**config)
# For libraries, should be your library's `__name__`
logger.disable("my_library")
logger.info("No matter added sinks, this message is not displayed")
# In your application, enable the logger in the library
logger.enable("my_library")
logger.info("This message however is propagated to the sinks")
```
For additional convenience, you can also use the [`loguru-config`](https://github.com/erezinman/loguru-config) library to setup the `logger` directly from a configuration file.
### Entirely compatible with standard logging
Wish to use built-in logging `Handler` as a Loguru sink?
```python
handler = logging.handlers.SysLogHandler(address=('localhost', 514))
logger.add(handler)
```
Need to propagate Loguru messages to standard `logging`?
```python
class PropagateHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
logging.getLogger(record.name).handle(record)
logger.add(PropagateHandler(), format="{message}")
```
Want to intercept standard `logging` messages toward your Loguru sinks?
```python
class InterceptHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
# Get corresponding Loguru level if it exists.
try:
level: str | int = logger.level(record.levelname).name
except ValueError:
level = record.levelno
# Find caller from where originated the logged message.
frame, depth = inspect.currentframe(), 0
while frame:
filename = frame.f_code.co_filename
is_logging = filename == logging.__file__
is_frozen = "importlib" in filename and "_bootstrap" in filename
if depth > 0 and not (is_logging or is_frozen):
break
frame = frame.f_back
depth += 1
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
```
### Personalizable defaults through environment variables
Don't like the default logger formatting? Would prefer another `DEBUG` color? [No problem](https://loguru.readthedocs.io/en/stable/api/logger.html#env):
```bash
# Linux / OSX
export LOGURU_FORMAT="{time} | <lvl>{message}</lvl>"
# Windows
setx LOGURU_DEBUG_COLOR "<green>"
```
Environment variables match the arguments of [`logger.add()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add), with `LOGURU_LEVEL` notably letting you set the default level of handlers. Common flags like `NO_COLOR` and `FORCE_COLOR` are also supported.
### Convenient parser
It is often useful to extract specific information from generated logs, this is why Loguru provides a [`parse()`](https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.parse) method which helps to deal with logs and regexes.
```python
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups
caster_dict = dict(time=dateutil.parser.parse, level=int) # Transform matching groups
for groups in logger.parse("file.log", pattern, cast=caster_dict):
print("Parsed:", groups)
# {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
```
### Exhaustive notifier
Loguru can easily be combined with the great [`apprise`](https://github.com/caronc/apprise) library (must be installed separately) to receive an e-mail when your program fail unexpectedly or to send many other kind of notifications.
```python
import apprise
# Define the configuration constants.
WEBHOOK_ID = "123456790"
WEBHOOK_TOKEN = "abc123def456"
# Prepare the object to send Discord notifications.
notifier = apprise.Apprise()
notifier.add(f"discord://{WEBHOOK_ID}/{WEBHOOK_TOKEN}")
# Install a handler to be alerted on each error.
# You can filter out logs from "apprise" itself to avoid recursive calls.
logger.add(notifier.notify, level="ERROR", filter={"apprise": False})
```
This can be seamlessly integrated using the [`logprise`](https://github.com/svaningelgem/logprise) library.
<s>
### 10x faster than built-in logging
</s>
Although logging impact on performances is in most cases negligible, a zero-cost logger would allow to use it anywhere without much concern. In an upcoming release, Loguru's critical functions will be implemented in C for maximum speed.
<!-- end-of-readme-usage -->
## Documentation
- [API Reference](https://loguru.readthedocs.io/en/stable/api/logger.html)
- [Help & Guides](https://loguru.readthedocs.io/en/stable/resources.html)
- [Type hints](https://loguru.readthedocs.io/en/stable/api/type_hints.html)
- [Contributing](https://loguru.readthedocs.io/en/stable/project/contributing.html)
- [License](https://loguru.readthedocs.io/en/stable/project/license.html)
- [Changelog](https://loguru.readthedocs.io/en/stable/project/changelog.html)
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = loguru
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/_static/css/loguru.css
================================================
.wy-nav-content {
/* By default it's hardcoded to 800px.
We increase it a bit so code source fits without horizontal scrolling. */
max-width: 850px;
}
================================================
FILE: docs/_static/js/copybutton.js
================================================
$(document).ready(function() {
/* Add a [>>>] button on the top-right corner of code samples to hide
* the >>> and ... prompts and the output and thus make the code
* copyable. */
var hig = $('.highlight-default')
var div = hig.find('.highlight')
var pre = div.find('pre');
// get the styles from the current theme
pre.parent().parent().css('position', 'relative');
var hide_text = 'Hide the prompts and output';
var show_text = 'Show the prompts and output';
var border_width = hig.css('border-top-width');
var border_style = hig.css('border-top-style');
var border_color = hig.css('border-top-color');
var button_styles = {
'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0',
'border-color': border_color, 'border-style': border_style,
'border-width': border_width, 'color': border_color, 'text-size': '75%',
'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em',
'border-radius': '0', 'border-width': '0px 0px 1px 1px',
'line-height': '14px', 'font-size': '12px'
}
// create and add the button to all the code blocks that contain >>>
div.each(function(index) {
var jthis = $(this);
if (jthis.find('.gp').length > 0) {
var button = $('<span class="copybutton">>>></span>');
button.css(button_styles)
button.attr('title', hide_text);
button.data('hidden', 'false');
jthis.prepend(button);
}
// tracebacks (.gt) contain bare text elements that need to be
// wrapped in a span to work with .nextUntil() (see later)
jthis.find('pre:has(.gt)').contents().filter(function() {
return ((this.nodeType == 3) && (this.data.trim().length > 0));
}).wrap('<span>');
});
// define the behavior of the button when it's clicked
$('.copybutton').click(function(e){
e.preventDefault();
var button = $(this);
if (button.data('hidden') === 'false') {
// hide the code output
button.parent().find('.go, .gp, .gt').hide();
button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');
button.css('text-decoration', 'line-through');
button.attr('title', show_text);
button.data('hidden', 'true');
} else {
// show the code output
button.parent().find('.go, .gp, .gt').show();
button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');
button.css('text-decoration', 'none');
button.attr('title', hide_text);
button.data('hidden', 'false');
}
});
});
================================================
FILE: docs/_templates/breadcrumbs.html
================================================
{%- extends "sphinx_rtd_theme/breadcrumbs.html" %}
{% block breadcrumbs_aside %}
{% endblock %}
================================================
FILE: docs/_templates/layout.html
================================================
{% extends '!layout.html' %}
{% block document %}
{{super()}}
<div class="github-corner">
<svg width="120" height="120" viewBox="0 0 250 250">
<a fill="transparent" href="https://github.com/{{github_user}}/{{github_repo}}" style="pointer-events:auto">
<path d="M0,0 L250,250 L250,0 Z"></path>
</a>
<g class="octocat">
<path fill="#343131" d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" ></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="#ffffff" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="#ffffff" class="octo-body"></path>
<text x="240px" y="-10px" class="octo-click-text" stroke="#ffffff" fill="#ffffff">CLICK</text>
<g class="octo-glasses" visibility="hidden">
<svg fill="#343131" width="640" height="480">
<defs>
<symbol id="glasses" viewBox="0 0 512 512">
<path d="m465.4,247c-2.2,-22 -12.4,-43 -28.9,-58.4c-17.1,-15.9 -39.3,-24.7 -62.7,-24.7c-41.5,0 -77.3,27.4 -88.5,67c-7,-7 -18.5,-11.7 -29.3,-11.7c-10.8,0 -22.3,4.7 -29.3,11.7c-11.2,-39.6 -47,-67 -88.5,-67c-23.3,0 -45.6,8.7 -62.7,24.6c-16.5,15.5 -26.7,36.5 -28.9,58.5l-14.6,0l0,18l14.6,0c2.2,22 12.4,43 28.9,58.4c17.1,15.9 39.3,24.7 62.7,24.7c50.8,0 92.1,-41.2 92.1,-92c0,-0.1 0,-0.1 0,-0.1l0,0c0,-9.9 11.5,-21.6 25.7,-21.6s25.7,11.7 25.7,21.6l0,0c0,0 0,0 0,0.1c0,50.8 41.3,92 92.1,92c23.3,0 45.6,-8.7 62.7,-24.7c16.5,-15.4 26.7,-36.5 28.9,-58.5l14.6,0l0,-18l-14.6,0l0,0.1z" />
</symbol>
</defs>
<g>
<use x="530" y="-70" xlink:href="#glasses" transform="rotate(45 100,75) matrix(0.185,0,0,0.185,0,0) " />
</g>
</svg>
</g>
</g>
</svg>
</div>
<style>
.github-corner {
pointer-events: none;
position: absolute;
top: 0;
right: 0;
border: 0;
mix-blend-mode: darken;
}
.github-corner:hover .octocat{
transform-origin: center;
animation: octocat-grow 5s ease-in-out forwards;
}
@keyframes octocat-grow{
16%, 66%{
transform: scale(1.2);
}
33%{
transform: scale(1.1);
}
50%, 100%{
transform: scale(1.3);
}
83%{
transform: scale(1.4);
}
}
.octo-click-text {
font-weight: normal;
text-anchor: middle;
font-family: 'Avenir', Helvetica, Arial, sans-serif;
font-size: 20px;
stroke-width: 2px;
transform: rotate(45deg);
opacity: 0;
}
.github-corner:hover .octo-click-text {
animation: octocat-text 5s linear forwards
}
@keyframes octocat-text {
99% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.github-corner:hover .octo-arm{
animation: octocat-wave 400ms linear infinite;
}
@keyframes octocat-wave {
0%,50%,100% {
transform: rotate(0)
}
25% {
transform: rotate(-25deg)
}
75% {
transform: rotate(10deg)
}
}
.github-corner .octo-glasses{
visibility: visible;
opacity: 0;
transition: opacity 5s;
}
.github-corner:hover .octo-glasses{
opacity: 1;
animation: octocat-glass-wiggle 400ms linear forwards;
animation-delay: 4s;
}
@keyframes octocat-glass-wiggle {
0%, 50%, 100% {
transform: translateX(0px) translateY(0px);
}
25%, 75% {
transform: translateX(3px) translateY(-3px);
}
}
</style>
{% endblock %}
================================================
FILE: docs/api/logger.rst
================================================
``loguru.logger``
=================
.. automodule:: loguru._logger
.. autoclass:: loguru._logger.Logger()
:members:
================================================
FILE: docs/api/type_hints.rst
================================================
.. _type-hints:
Type Hints
==========
.. |str| replace:: :class:`str`
.. |namedtuple| replace:: :func:`namedtuple<collections.namedtuple>`
.. |dict| replace:: :class:`dict`
.. |Logger| replace:: :class:`~loguru._logger.Logger`
.. |catch| replace:: :meth:`~loguru._logger.Logger.catch()`
.. |contextualize| replace:: :meth:`~loguru._logger.Logger.contextualize()`
.. |complete| replace:: :meth:`~loguru._logger.Logger.complete()`
.. |bind| replace:: :meth:`~loguru._logger.Logger.bind()`
.. |patch| replace:: :meth:`~loguru._logger.Logger.patch()`
.. |opt| replace:: :meth:`~loguru._logger.Logger.opt()`
.. |level| replace:: :meth:`~loguru._logger.Logger.level()`
.. _stub file: https://www.python.org/dev/peps/pep-0484/#stub-files
.. _string literals: https://www.python.org/dev/peps/pep-0484/#forward-references
.. _postponed evaluation of annotations: https://www.python.org/dev/peps/pep-0563/
.. |future| replace:: ``__future__``
.. _future: https://www.python.org/dev/peps/pep-0563/#enabling-the-future-behavior-in-python-3-7
.. |loguru-mypy| replace:: ``loguru-mypy``
.. _loguru-mypy: https://github.com/kornicameister/loguru-mypy
.. |documentation of loguru-mypy| replace:: documentation of ``loguru-mypy``
.. _documentation of loguru-mypy:
https://github.com/kornicameister/loguru-mypy/blob/master/README.md
.. _@kornicameister: https://github.com/kornicameister
Loguru relies on a `stub file`_ to document its types. This implies that these types are not
accessible during execution of your program, however they can be used by type checkers and IDE.
Also, this means that your Python interpreter has to support `postponed evaluation of annotations`_
to prevent error at runtime. This is achieved with a |future|_ import in Python 3.7+ or by using
`string literals`_ for earlier versions.
A basic usage example could look like this:
.. code-block:: python
from __future__ import annotations
import loguru
from loguru import logger
def good_sink(message: loguru.Message):
print("My name is", message.record["name"])
def bad_filter(record: loguru.Record):
return record["invalid"]
logger.add(good_sink, filter=bad_filter)
.. code-block::
$ mypy test.py
test.py:8: error: TypedDict "Record" has no key 'invalid'
Found 1 error in 1 file (checked 1 source file)
There are several internal types to which you can be exposed using Loguru's public API, they are
listed here and might be useful to type hint your code:
- ``Logger``: the usual |logger| object (also returned by |opt|, |bind| and |patch|).
- ``Message``: the formatted logging message sent to the sinks (a |str| with ``record``
attribute).
- ``Record``: the |dict| containing all contextual information of the logged message.
- ``Level``: the |namedtuple| returned by |level| (with ``name``, ``no``, ``color`` and ``icon``
attributes).
- ``Catcher``: the context decorator returned by |catch|.
- ``Contextualizer``: the context decorator returned by |contextualize|.
- ``AwaitableCompleter``: the awaitable object returned by |complete|.
- ``RecordFile``: the ``record["file"]`` with ``name`` and ``path`` attributes.
- ``RecordLevel``: the ``record["level"]`` with ``name``, ``no`` and ``icon`` attributes.
- ``RecordThread``: the ``record["thread"]`` with ``id`` and ``name`` attributes.
- ``RecordProcess``: the ``record["process"]`` with ``id`` and ``name`` attributes.
- ``RecordException``: the ``record["exception"]`` with ``type``, ``value`` and ``traceback``
attributes.
If that is not enough, one can also use the |loguru-mypy|_ library developed by `@kornicameister`_.
Plugin can be installed separately using::
pip install loguru-mypy
It helps to catch several possible runtime errors by performing additional checks like:
- ``opt(lazy=True)`` loggers accepting only ``typing.Callable[[], typing.Any]`` arguments
- ``opt(record=True)`` loggers wrongly calling log handler like so ``logger.info(..., record={})``
- and even more...
For more details, go to official |documentation of loguru-mypy|_.
See also: :ref:`type-hints-source`.
================================================
FILE: docs/api/type_hints_source.rst
================================================
:orphan:
.. _type-hints-source:
Source Code of Type Hints
=========================
.. include:: ../../loguru/__init__.pyi
:literal:
================================================
FILE: docs/api.rst
================================================
API Reference
=============
.. automodule:: loguru
.. toctree::
:hidden:
:includehidden:
api/logger.rst
api/type_hints.rst
* :class:`~loguru._logger.Logger`
* :meth:`~loguru._logger.Logger.add`
* :ref:`sink`
* :ref:`message`
* :ref:`levels`
* :ref:`record`
* :ref:`time`
* :ref:`file`
* :ref:`color`
* :ref:`env`
* :meth:`~loguru._logger.Logger.remove`
* :meth:`~loguru._logger.Logger.complete`
* :meth:`~loguru._logger.Logger.catch`
* :meth:`~loguru._logger.Logger.opt`
* :meth:`~loguru._logger.Logger.bind`
* :meth:`~loguru._logger.Logger.contextualize`
* :meth:`~loguru._logger.Logger.patch`
* :meth:`~loguru._logger.Logger.level`
* :meth:`~loguru._logger.Logger.disable`
* :meth:`~loguru._logger.Logger.enable`
* :meth:`~loguru._logger.Logger.configure`
* :meth:`~loguru._logger.Logger.reinstall`
* :meth:`~loguru._logger.Logger.parse`
* :meth:`~loguru._logger.Logger.trace`
* :meth:`~loguru._logger.Logger.debug`
* :meth:`~loguru._logger.Logger.info`
* :meth:`~loguru._logger.Logger.success`
* :meth:`~loguru._logger.Logger.warning`
* :meth:`~loguru._logger.Logger.error`
* :meth:`~loguru._logger.Logger.critical`
* :meth:`~loguru._logger.Logger.log`
* :meth:`~loguru._logger.Logger.exception`
* :ref:`type-hints`
================================================
FILE: docs/conf.py
================================================
"""Configuration file for the Sphinx documentation builder.
This file does only contain a selection of the most common options. For a
full list see the documentation: http://www.sphinx-doc.org/en/master/config
-- Path setup --------------------------------------------------------------
If extensions (or modules to document with autodoc) are in another directory,
add these directories to sys.path here. If the directory is relative to the
documentation root, use os.path.abspath to make it absolute, like shown here.
"""
import os
import sys
sys.path.insert(0, os.path.abspath(".."))
# -- Project information -----------------------------------------------------
project = "loguru"
copyright = "2018, Delgan"
author = "Delgan"
# The short X.Y version
version = ""
# The full version, including alpha/beta/rc tags
release = ""
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"myst_parser",
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
"sphinx.ext.intersphinx",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = [".rst", ".md"]
# The master toctree document.
master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "English"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# Warn about all references where the target cannot be found.
nitpicky = True
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "logurudoc"
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [(master_doc, "loguru.tex", "loguru Documentation", "Delgan", "manual")]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "loguru", "loguru Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"loguru",
"loguru Documentation",
author,
"loguru",
"One line description of project.",
"Miscellaneous",
)
]
# -- Extension configuration -------------------------------------------------
html_context = {"github_user": "delgan", "github_repo": "loguru"}
add_module_names = False
autodoc_member_order = "bysource"
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
html_show_sourcelink = False
html_show_copyright = False
napoleon_use_rtype = False
napoleon_use_ivar = True
myst_heading_anchors = 3
# MyST parser complains that headers where the README start at H2 and not H1.
# We may be able to get rid of this warning if we convert the reST files to MyST.
suppress_warnings = ["myst.header"]
def setup(app):
"""Configure the generation of docs."""
app.add_css_file("css/loguru.css")
app.add_js_file("js/copybutton.js")
================================================
FILE: docs/index.rst
================================================
.. include:: ../README.md
:parser: myst_parser.sphinx_
:end-before: <!-- end-of-readme-intro -->
Table of Contents
=================
.. toctree::
:includehidden:
:maxdepth: 2
overview.rst
api.rst
resources.rst
project.rst
================================================
FILE: docs/overview.rst
================================================
Overview
========
.. include:: ../README.md
:parser: myst_parser.sphinx_
:start-after: <!-- end-of-readme-intro -->
:end-before: <!-- end-of-readme-usage -->
================================================
FILE: docs/project/changelog.rst
================================================
Changelog
#########
.. include:: ../../CHANGELOG.rst
================================================
FILE: docs/project/contributing.rst
================================================
Contributing
############
.. include:: ../../CONTRIBUTING.rst
================================================
FILE: docs/project/license.rst
================================================
License
#######
.. literalinclude:: ../../LICENSE
:language: none
================================================
FILE: docs/project.rst
================================================
Project Information
===================
.. toctree::
project/contributing.rst
project/license.rst
project/changelog.rst
================================================
FILE: docs/resources/migration.rst
================================================
Switching from Standard Logging to Loguru
=========================================
.. highlight:: python3
.. |getLogger| replace:: :func:`~logging.getLogger`
.. |addLevelName| replace:: :func:`~logging.addLevelName`
.. |getLevelName| replace:: :func:`~logging.getLevelName`
.. |Handler| replace:: :class:`~logging.Handler`
.. |Logger| replace:: :class:`~logging.Logger`
.. |Filter| replace:: :class:`~logging.Filter`
.. |Formatter| replace:: :class:`~logging.Formatter`
.. |LoggerAdapter| replace:: :class:`~logging.LoggerAdapter`
.. |LogRecord| replace:: :class:`~logging.LogRecord`
.. |logger.setLevel| replace:: :meth:`~logging.Logger.setLevel`
.. |logger.addFilter| replace:: :meth:`~logging.Logger.addFilter`
.. |makeRecord| replace:: :meth:`~logging.Logger.makeRecord`
.. |disable| replace:: :func:`~logging.disable`
.. |setLogRecordFactory| replace:: :func:`~logging.setLogRecordFactory`
.. |propagate| replace:: :attr:`~logging.Logger.propagate`
.. |addHandler| replace:: :meth:`~logging.Logger.addHandler`
.. |removeHandler| replace:: :meth:`~logging.Logger.removeHandler`
.. |handle| replace:: :meth:`~logging.Handler.handle`
.. |emit| replace:: :meth:`~logging.Handler.emit`
.. |handler.setLevel| replace:: :meth:`~logging.Handler.setLevel`
.. |handler.addFilter| replace:: :meth:`~logging.Handler.addFilter`
.. |setFormatter| replace:: :meth:`~logging.Handler.setFormatter`
.. |createLock| replace:: :meth:`~logging.Handler.createLock`
.. |acquire| replace:: :meth:`~logging.Handler.acquire`
.. |release| replace:: :meth:`~logging.Handler.release`
.. |isEnabledFor| replace:: :meth:`~logging.Logger.isEnabledFor`
.. |dictConfig| replace:: :func:`~logging.config.dictConfig`
.. |basicConfig| replace:: :func:`~logging.basicConfig`
.. |captureWarnings| replace:: :func:`~logging.captureWarnings`
.. |assertLogs| replace:: :meth:`~unittest.TestCase.assertLogs`
.. |unittest| replace:: :mod:`unittest`
.. |warnings| replace:: :mod:`warnings`
.. |warnings.showwarning| replace:: :func:`warnings.showwarning`
.. |add| replace:: :meth:`~loguru._logger.Logger.add()`
.. |remove| replace:: :meth:`~loguru._logger.Logger.remove()`
.. |bind| replace:: :meth:`~loguru._logger.Logger.bind`
.. |patch| replace:: :meth:`~loguru._logger.Logger.patch`
.. |opt| replace:: :meth:`~loguru._logger.Logger.opt()`
.. |level| replace:: :meth:`~loguru._logger.Logger.level()`
.. |configure| replace:: :meth:`~loguru._logger.Logger.configure()`
.. |pytest| replace:: ``pytest``
.. _pytest: https://docs.pytest.org/en/latest/
.. |conftest.py| replace:: ``conftest.py``
.. _conftest.py: https://docs.pytest.org/en/latest/reference/fixtures.html#conftest-py-sharing-fixtures-across-multiple-files
.. |caplog| replace:: ``caplog``
.. _caplog: https://docs.pytest.org/en/latest/logging.html?highlight=caplog#caplog-fixture
.. |pytest-loguru| replace:: ``pytest-loguru``
.. _pytest-loguru: https://github.com/mcarans/pytest-loguru
.. _@mcarans: https://github.com/mcarans
Introduction to logging in Python
---------------------------------
First and foremost, it is important to understand some basic concepts about logging in Python.
Logging is an essential part of any application, as it allows you to track the behavior of your code and diagnose issues. It associates messages with severity levels which are collected and dispatched to readable outputs called handlers.
For newcomers, take a look at the tutorial in the Python documentation: `Logging HOWTO <https://docs.python.org/3/howto/logging.html>`_.
Fundamental differences between ``logging`` and ``loguru``
----------------------------------------------------------
Although ``loguru`` is written "from scratch" and does not rely on standard ``logging`` internally, both libraries serve the same purpose: provide functionalities to implement a flexible event logging system. The main difference is that standard ``logging`` requires the user to explicitly instantiate named ``Logger`` and configure them with ``Handler``, ``Formatter`` and ``Filter``, while ``loguru`` tries to narrow down the amount of configuration steps.
Apart from that, usage is globally the same, once the ``logger`` object is created or imported you can start using it to log messages with the appropriate severity (``logger.debug("Dev message")``, ``logger.warning("Danger!")``, etc.), messages which are then sent to the configured handlers.
As for standard logging, default logs are sent to ``sys.stderr`` rather than ``sys.stdout``. The POSIX standard specifies that ``stderr`` is the correct stream for "diagnostic output". The main compelling case in favor or logging to ``stderr`` is that it avoids mixing the actual output of the application with debug information. Consider for example pipe-redirection like ``python my_app.py | other_app`` which would not be possible if logs were emitted to ``stdout``. Another major benefit is that Python resolves encoding issues on ``sys.stderr`` by escaping faulty characters (``"backslashreplace"`` policy) while it raises an ``UnicodeEncodeError`` (``"strict"`` policy) on ``sys.stdout``.
Replacing ``getLogger()`` function
----------------------------------
It is usual to call |getLogger| at the beginning of each file to retrieve and use a logger across your module, like this: ``logger = logging.getLogger(__name__)``.
Using Loguru, there is no need to explicitly get and name a logger, ``from loguru import logger`` suffices. Each time this imported logger is used, a :ref:`record <record>` is created and will automatically contain the contextual ``__name__`` value.
As for standard logging, the ``name`` attribute can then be used to format and filter your logs.
Replacing ``Logger`` objects
----------------------------
Loguru replaces the standard |Logger| configuration by a proper :ref:`sink <sink>` definition. Instead of configuring a logger, you should |add| and parametrize your handlers. The |logger.setLevel| and |logger.addFilter| are suppressed by the configured sink ``level`` and ``filter`` parameters. The |propagate| attribute and |disable| function can be replaced by the ``filter`` option too. The |makeRecord| method can be replaced using the ``record["extra"]`` dict.
Sometimes, more fine-grained control is required over a particular logger. In such case, Loguru provides the |bind| method which can be in particular used to generate a specifically named logger.
For example, by calling ``other_logger = logger.bind(name="other")``, each :ref:`message <message>` logged using ``other_logger`` will populate the ``record["extra"]`` dict with the ``name`` value, while using ``logger`` won't. This permits differentiating logs from ``logger`` or ``other_logger`` from within your sink or filter function.
Let suppose you want a sink to log only some very specific messages::
def specific_only(record):
return "specific" in record["extra"]
logger.add("specific.log", filter=specific_only)
specific_logger = logger.bind(specific=True)
logger.info("General message") # This is filtered-out by the specific sink
specific_logger.info("Module message") # This is accepted by the specific sink (and others)
Another example, if you want to attach one sink to one named logger::
# Only write messages from "a" logger
logger.add("a.log", filter=lambda record: record["extra"].get("name") == "a")
# Only write messages from "b" logger
logger.add("b.log", filter=lambda record: record["extra"].get("name") == "b")
logger_a = logger.bind(name="a")
logger_b = logger.bind(name="b")
logger_a.info("Message A")
logger_b.info("Message B")
Replacing ``Handler``, ``Filter`` and ``Formatter`` objects
-----------------------------------------------------------
Standard ``logging`` requires you to create an |Handler| object and then call |addHandler|. Using Loguru, the handlers are started using |add|. The sink defines how the handler should manage incoming logging messages, as would do |handle| or |emit|. To log from multiple modules, you just have to import the logger, all messages will be dispatched to the added handlers.
While calling |add|, the ``level`` parameter replaces |handler.setLevel|, the ``format`` parameter replaces |setFormatter|, the ``filter`` parameter replaces |handler.addFilter|. The thread-safety is managed automatically by Loguru, so there is no need for |createLock|, |acquire| nor |release|. The equivalent method of |removeHandler| is |remove| which should be used with the identifier returned by |add|.
Note that you don't necessarily need to replace your |Handler| objects because |add| accepts them as valid sinks.
In short, you can replace::
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler("spam.log")
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)
With::
fmt = "{time} - {name} - {level} - {message}"
logger.add("spam.log", level="DEBUG", format=fmt)
logger.add(sys.stderr, level="ERROR", format=fmt)
Replacing ``LogRecord`` objects
-------------------------------
In Loguru, the equivalence of a |LogRecord| instance is a simple ``dict`` which stores the details of a logged message. To find the correspondence with |LogRecord| attributes, please refer to :ref:`the "record dict" documentation <record>` which lists all available keys.
This ``dict`` is attached to each :ref:`logged message <message>` through a special ``record`` attribute of the ``str``-like object received by sinks. For example::
def simple_sink(message):
# A simple sink can use "message" as a basic string and ignore the "record" attribute.
print(message, end="")
def advanced_sink(message):
# An advanced sink can use the "record" attribute to access contextual information.
record = message.record
if record["level"].no >= 50:
file_path = record["file"].path
print(f"Critical error in {file_path}", end="", file=sys.stderr)
else:
print(message, end="")
logger.add(simple_sink)
logger.add(advanced_sink)
As explained in the previous sections, the record dict is also available during invocation of filtering and formatting functions.
If you need to extend the record dict with custom information similarly to what was possible with |setLogRecordFactory|, you can simply use the |patch| method to add the desired keys to the ``record["extra"]`` dict.
Replacing ``%`` style formatting of messages
--------------------------------------------
Loguru only supports ``{}``-style formatting.
You have to replace ``logger.debug("Some variable: %s", var)`` with ``logger.debug("Some variable: {}", var)``. All ``*args`` and ``**kwargs`` passed to a logging function are used to call ``message.format(*args, **kwargs)``. Arguments which do not appear in the message string are simply ignored. Note that passing arguments to logging functions like this may be useful to (slightly) improve performances: it avoids formatting the message if the level is too low to pass any configured handler.
For converting the general format used by |Formatter|, refer to :ref:`list of available record tokens <record>`.
For converting the date format used by ``datefmt``, refer to :ref:`list of available date tokens<time>`.
Replacing ``exc_info`` argument
-------------------------------
While calling standard logging function, you can pass ``exc_info`` as an argument to add stacktrace to the message. Instead of that, you should use the |opt| method with ``exception`` parameter, replacing ``logger.debug("Debug error:", exc_info=True)`` with ``logger.opt(exception=True).debug("Debug error:")``.
The formatted exception will include the whole stacktrace and variables. To prevent that, make sure to use ``backtrace=False`` and ``diagnose=False`` while adding your sink.
Replacing ``extra`` argument and ``LoggerAdapter`` objects
----------------------------------------------------------
To pass contextual information to log messages, replace ``extra`` by inlining |bind| method::
context = {"clientip": "192.168.0.1", "user": "fbloggs"}
logger.info("Protocol problem", extra=context) # Standard logging
logger.bind(**context).info("Protocol problem") # Loguru
This will add context information to the ``record["extra"]`` dict of your logged message, so make sure to configure your handler format adequately::
fmt = "%(asctime)s %(clientip)s %(user)s %(message)s" # Standard logging
fmt = "{time} {extra[clientip]} {extra[user]} {message}" # Loguru
You can also replace |LoggerAdapter| by calling ``logger = logger.bind(clientip="192.168.0.1")`` before using it, or by assigning the bound logger to a class instance::
class MyClass:
def __init__(self, clientip):
self.logger = logger.bind(clientip=clientip)
def func(self):
self.logger.debug("Running func")
Replacing ``isEnabledFor()`` method
-----------------------------------
If you wish to log useful information for your debug logs, but don't want to pay the performance penalty in release mode while no debug handler is configured, standard logging provides the |isEnabledFor| method::
if logger.isEnabledFor(logging.DEBUG):
logger.debug("Message data: %s", expensive_func())
You can replace this with the |opt| method and ``lazy`` option::
# Arguments should be functions which will be called if needed
logger.opt(lazy=True).debug("Message data: {}", expensive_func)
Replacing ``addLevelName()`` and ``getLevelName()`` functions
-------------------------------------------------------------
To add a new custom level, you can replace |addLevelName| with the |level| function::
logging.addLevelName(33, "CUSTOM") # Standard logging
logger.level("CUSTOM", no=45, color="<red>", icon="🚨") # Loguru
The same function can be used to replace |getLevelName|::
logger.getLevelName(33) # => "CUSTOM"
logger.level("CUSTOM") # => (name='CUSTOM', no=33, color="<red>", icon="🚨")
Note that contrary to standard logging, Loguru doesn't associate severity number to any level, levels are only identified by their name.
Replacing ``basicConfig()`` and ``dictConfig()`` functions
----------------------------------------------------------
The |basicConfig| and |dictConfig| functions are replaced by the |configure| method.
This does not accept ``config.ini`` files, though, so you have to handle that yourself using your favorite format.
Replacing ``captureWarnings()`` function
----------------------------------------
The |captureWarnings| function which redirects alerts from the |warnings| module to the logging system can be implemented by simply replacing |warnings.showwarning| function as follow::
import warnings
from loguru import logger
showwarning_ = warnings.showwarning
def showwarning(message, *args, **kwargs):
logger.warning(message)
showwarning_(message, *args, **kwargs)
warnings.showwarning = showwarning
.. _migration-assert-logs:
Replacing ``assertLogs()`` method from ``unittest`` library
-----------------------------------------------------------
The |assertLogs| method defined in the |unittest| from standard library is used to capture and test logged messages. However, it can't be made compatible with Loguru. It needs to be replaced with a custom context manager possibly implemented as follows::
from contextlib import contextmanager
@contextmanager
def capture_logs(level="INFO", format="{level}:{name}:{message}"):
"""Capture loguru-based logs."""
output = []
handler_id = logger.add(output.append, level=level, format=format)
yield output
logger.remove(handler_id)
It provides the list of :ref:`logged messages <message>` for each of which you can access :ref:`the record attribute<record>`. Here is a usage example::
def do_something(val):
if val < 0:
logger.error("Invalid value")
return 0
return val * 2
class TestDoSomething(unittest.TestCase):
def test_do_something_good(self):
with capture_logs() as output:
do_something(1)
self.assertEqual(output, [])
def test_do_something_bad(self):
with capture_logs() as output:
do_something(-1)
self.assertEqual(len(output), 1)
message = output[0]
self.assertIn("Invalid value", message)
self.assertEqual(message.record["level"].name, "ERROR")
.. seealso::
See :ref:`testing logging <recipes-testing>` for alternative approaches.
.. _migration-caplog:
Replacing ``caplog`` fixture from ``pytest`` library
----------------------------------------------------
|pytest|_ is a very common testing framework. The |caplog|_ fixture captures logging output so that it can be tested against. For example::
from loguru import logger
def some_func(a, b):
if a < 0:
logger.warning("Oh no!")
return a + b
def test_some_func(caplog):
assert some_func(-1, 3) == 2
assert "Oh no!" in caplog.text
If you've followed all the migration guidelines thus far, you'll notice that this test will fail. This is because |pytest|_ links to the standard library's ``logging`` module.
To ensure compatibility between Loguru and |pytest|_, we need to propagate logs to the standard logging handlers used by |pytest|_. We simply need to |add| these handlers to Loguru's logger using a custom fixture:
* The ``caplog_handler`` handler which captures logs for inspection in tests using the |caplog|_ fixture.
* The ``log_cli_handler`` which outputs logs in live during tests when the ``--log-cli-level`` flag is used.
* The ``report_handler`` which displays logs in the terminal summary at the end of tests in case of failures.
In your |conftest.py|_ file, add the following::
import pytest
from loguru import logger
@pytest.fixture(autouse=True)
def propagate_logs(request):
"""Propagate Loguru logs to standard logging handlers used by Pytest."""
plugin = request.config.pluginmanager.getplugin("logging-plugin")
# Remove all existing Loguru handlers, including the default one.
logger.remove()
handler_ids = []
for handler in [plugin.caplog_handler, plugin.log_cli_handler, plugin.report_handler]:
# Note that, by default, all log levels are propagated to standard handlers.
# You can adjust the `level` here, modify the handler's level, or use `caplog.set_level()`.
handler_id = logger.add(handler, format="{message}", level=0)
handler_ids.append(handler_id)
yield
for handler_id in handler_ids:
logger.remove(handler_id)
Run your tests and things should all be working as expected. See also `How to manage logging <https://docs.pytest.org/en/stable/how-to/logging.html>`_ from the official Pytest documentation.
You can also install and use the |pytest-loguru|_ package created by `@mcarans`_.
.. seealso::
See :ref:`testing logging <recipes-testing>` for alternative approaches.
================================================
FILE: docs/resources/recipes.rst
================================================
Code Snippets and Recipes for Loguru
====================================
.. highlight:: python3
.. |print| replace:: :func:`print()`
.. |open| replace:: :func:`open()`
.. |sys.__stdout__| replace:: :data:`sys.__stdout__`
.. |sys.stdout| replace:: :data:`sys.stdout`
.. |sys.stderr| replace:: :data:`sys.stderr`
.. |warnings| replace:: :mod:`warnings`
.. |warnings.showwarning| replace:: :func:`warnings.showwarning`
.. |warnings.warn| replace:: :func:`warnings.warn`
.. |contextlib.redirect_stdout| replace:: :func:`contextlib.redirect_stdout`
.. |copy.deepcopy| replace:: :func:`copy.deepcopy`
.. |os.fork| replace:: :func:`os.fork`
.. |os.umask| replace:: :func:`os.umask`
.. |multiprocessing| replace:: :mod:`multiprocessing`
.. |pickle| replace:: :mod:`pickle`
.. |traceback| replace:: :mod:`traceback`
.. |Thread| replace:: :class:`~threading.Thread`
.. |Process| replace:: :class:`~multiprocessing.Process`
.. |Pool| replace:: :class:`~multiprocessing.pool.Pool`
.. |Pool.map| replace:: :meth:`~multiprocessing.pool.Pool.map`
.. |Pool.apply| replace:: :meth:`~multiprocessing.pool.Pool.apply`
.. |sys.stdout.reconfigure| replace:: :meth:`sys.stdout.reconfigure() <io.TextIOWrapper.reconfigure>`
.. |UnicodeEncodeError| replace:: :exc:`UnicodeEncodeError`
.. |add| replace:: :meth:`~loguru._logger.Logger.add()`
.. |remove| replace:: :meth:`~loguru._logger.Logger.remove()`
.. |enable| replace:: :meth:`~loguru._logger.Logger.enable()`
.. |disable| replace:: :meth:`~loguru._logger.Logger.disable()`
.. |bind| replace:: :meth:`~loguru._logger.Logger.bind()`
.. |patch| replace:: :meth:`~loguru._logger.Logger.patch()`
.. |opt| replace:: :meth:`~loguru._logger.Logger.opt()`
.. |log| replace:: :meth:`~loguru._logger.Logger.log()`
.. |level| replace:: :meth:`~loguru._logger.Logger.level()`
.. |configure| replace:: :meth:`~loguru._logger.Logger.configure()`
.. |complete| replace:: :meth:`~loguru._logger.Logger.complete()`
.. _`unicode`: https://docs.python.org/3/howto/unicode.html
.. |if-name-equals-main| replace:: ``if __name__ == "__main__":``
.. _if-name-equals-main: https://docs.python.org/3/library/__main__.html#idiomatic-usage
.. |logot| replace:: ``logot``
.. _logot: https://logot.readthedocs.io/
.. |pytest| replace:: ``pytest``
.. _pytest: https://docs.pytest.org/en/latest/
.. |stackprinter| replace:: ``stackprinter``
.. _stackprinter: https://github.com/cknd/stackprinter
.. |zmq| replace:: ``zmq``
.. _zmq: https://github.com/zeromq/pyzmq
.. _`GH#132`: https://github.com/Delgan/loguru/issues/132
Security considerations when using Loguru
-----------------------------------------
Firstly, if you use |pickle| to load log messages (e.g. from the network), make sure the source is trustable or sign the data to verify its authenticity before deserializing it. If you do not take these precautions, malicious code could be executed by an attacker. You can read more details in this article: `What’s so dangerous about pickles? <https://intoli.com/blog/dangerous-pickles/>`_
.. code::
import hashlib
import hmac
import pickle
def client(connection):
data = pickle.dumps("Log message")
digest = hmac.digest(b"secret-shared-key", data, hashlib.sha1)
connection.send(digest + b" " + data)
def server(connection):
expected_digest, data = connection.read().split(b" ", 1)
data_digest = hmac.digest(b"secret-shared-key", data, hashlib.sha1)
if not hmac.compare_digest(data_digest, expected_digest):
print("Integrity error")
else:
message = pickle.loads(data)
logger.info(message)
You should also avoid logging a message that could be maliciously hand-crafted by an attacker. Calling ``logger.debug(message, value)`` is roughly equivalent to calling ``print(message.format(value))`` and the same safety rules apply. In particular, an attacker could force printing of assumed hidden variables of your application. Here is an article explaining the possible vulnerability: `Be Careful with Python's New-Style String Format <https://lucumr.pocoo.org/2016/12/29/careful-with-str-format/>`_.
.. code::
SECRET_KEY = 'Y0UC4NTS33Th1S!'
class SomeValue:
def __init__(self, value):
self.value = value
# If user types "{value.__init__.__globals__[SECRET_KEY]}" then the secret key is displayed.
message = "[Custom message] " + input()
logger.info(message, value=SomeValue(10))
Another danger due to external input is the possibility of a log injection attack. Consider that you may need to escape user values before logging them: `Is your Python code vulnerable to log injection? <https://dev.arie.bovenberg.net/blog/is-your-python-code-vulnerable-to-log-injection/>`_
.. code::
logger.add("file.log", format="{level} {message}")
# If value is "Josh logged in.\nINFO User James" then there will appear to be two log entries.
username = external_data()
logger.info("User " + username + " logged in.")
Note that by default, Loguru will display the value of existing variables when an ``Exception`` is logged. This is very useful for debugging but could lead to credentials appearing in log files. Make sure to turn it off in production (or set the ``LOGURU_DIAGNOSE=NO`` environment variable).
.. code::
logger.add("out.log", diagnose=False)
Another thing you should consider is to change the access permissions of your log file. Loguru creates files using the built-in |open| function, which means by default they might be read by a different user than the owner. If this is not desirable, be sure to modify the default access rights.
.. code::
def opener(file, flags):
return os.open(file, flags, 0o600)
logger.add("combined.log", opener=opener)
Avoiding logs to be printed twice on the terminal
-------------------------------------------------
The logger is pre-configured for convenience with a default handler which writes messages to |sys.stderr|. You should |remove| it first if you plan to |add| another handler logging messages to the console, otherwise you may end up with duplicated logs.
.. code::
logger.remove() # Remove all handlers added so far, including the default one.
logger.add(sys.stderr, level="WARNING")
.. _changing-level-of-existing-handler:
Changing the level of an existing handler
-----------------------------------------
Once a handler has been added, it is actually not possible to update it. This is a deliberate choice in order to keep the Loguru's API minimal. Several solutions are possible, though, if you need to change the configured ``level`` of a handler. Chose the one that best fits your use case.
The most straightforward workaround is to |remove| your handler and then re-|add| it with the updated ``level`` parameter. To do so, you have to keep a reference to the identifier number returned while adding a handler::
handler_id = logger.add(sys.stderr, level="WARNING")
logger.info("Logging 'WARNING' or higher messages only")
...
logger.remove(handler_id) # For the default handler, it's actually '0'.
logger.add(sys.stderr, level="DEBUG")
logger.debug("Logging 'DEBUG' messages too")
Alternatively, you can combine the |bind| method with the ``filter`` argument to provide a function dynamically filtering logs based on their level::
def my_filter(record):
if record["extra"].get("warn_only"): # "warn_only" is bound to the logger and set to 'True'
return record["level"].no >= logger.level("WARNING").no
return True # Fallback to default 'level' configured while adding the handler
logger.add(sys.stderr, filter=my_filter, level="DEBUG")
# Use this logger first, debug messages are filtered out
logger = logger.bind(warn_only=True)
logger.warn("Initialization in progress")
# Then you can use this one to log all messages
logger = logger.bind(warn_only=False)
logger.debug("Back to debug messages")
Finally, more advanced control over handler's level can be achieved by using a callable object as the ``filter``::
min_level = logger.level("DEBUG").no
def filter_by_level(record):
return record["level"].no >= min_level
logger.remove()
logger.add(sys.stderr, filter=filter_by_level, level=0)
logger.debug("Logged")
min_level = logger.level("WARNING").no
logger.debug("Not logged")
Also, note that if the default handler configuration, such as the default level, does not suit your needs, it can be adjusted :ref:`through environment variables <env>`.
.. _configuring-loguru-as-lib-or-app:
Configuring Loguru to be used by a library or an application
------------------------------------------------------------
A clear distinction must be made between the use of Loguru within a library or an application.
In case of an application, you can add handlers from anywhere in your code. It's advised though to configure the logger from within a |if-name-equals-main|_ block inside the entry file of your script.
However, if your work is intended to be used as a library, you usually should not add any handler. This is user responsibility to configure logging according to its preferences, and it's better not to interfere with that. Indeed, since Loguru is based on a single common logger, handlers added by a library will also receive user logs, which is generally not desirable.
By default, a third-library should not emit logs except if specifically requested. For this reason, there exist the |disable| and |enable| methods. Make sure to first call ``logger.disable("mylib")``. This avoids library logs to be mixed with those of the user. The user can always call ``logger.enable("mylib")`` if he wants to access the logs of your library.
If you would like to ease logging configuration for your library users, it is advised to provide a function like ``configure_logger()`` in charge of adding the desired handlers. This will allow the user to activate the logging only if he needs to.
To summarize, let's look at this hypothetical package (none of the listed files are required, it all depends on how you plan your project to be used):
.. code:: text
mypackage
├── __init__.py
├── __main__.py
├── main.py
└── mymodule.py
Files relate to Loguru as follows:
* File ``__init__.py``:
* It is the entry point when your project is used as a library (``import mypackage``).
* It should contain ``logger.disable("mypackage")`` unconditionally at the top level.
* It should not call ``logger.add()`` as it modifies handlers configuration.
* File ``__main__.py``:
* It is the entry point when your project is used as an application (``python -m mypackage``).
* It can contain logging configuration unconditionally at the top level.
* File ``main.py``:
* It is the entry point when your project is used as a script (``python mypackage/main.py``).
* It can contain logging configuration inside an ``if __name__ == "__main__":`` block.
* File ``mymodule.py``:
* It is an internal module used by your project.
* It can use the ``logger`` simply by importing it.
* It does not need to configure anything.
.. _inter-process-communication:
Transmitting log messages across network, processes or Gunicorn workers
-----------------------------------------------------------------------
It is possible to send and receive logs between different processes and even between different computers if needed. Once the connection is established between the two Python programs, this requires serializing the logging record in one side while re-constructing the message on the other hand. Keep in mind though that `pickling is unsafe <https://intoli.com/blog/dangerous-pickles/>`_, you should use this with care.
The first thing you will need is to run a server responsible for receiving log messages emitted by other processes::
# server.py
import socketserver
import pickle
import struct
import sys
from loguru import logger
class LoggingRequestHandler(socketserver.StreamRequestHandler):
def handle(self):
while True:
chunk = self.connection.recv(4)
if len(chunk) < 4:
break
slen = struct.unpack(">L", chunk)[0]
chunk = self.connection.recv(slen)
while len(chunk) < slen:
chunk = chunk + self.connection.recv(slen - len(chunk))
record = pickle.loads(chunk)
level, message = record["level"].name, record["message"]
logger.patch(lambda r, record=record: r.update(record)).log(level, message)
if __name__ == "__main__":
# Configure the logger with desired handlers.
logger.configure(handlers=[{"sink": "server.log"}, {"sink": sys.stderr}])
# Setup the server to receive log messages from other processes.
with socketserver.TCPServer(("localhost", 9999), LoggingRequestHandler) as server:
server.serve_forever()
Then, you need your clients to send messages using a specific handler::
# client.py
import socket
import struct
import time
import pickle
from loguru import logger
class SocketHandler:
def __init__(self, host, port):
self._host = host
self._port = port
def write(self, message):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self._host, self._port))
record = message.record
data = pickle.dumps(record)
slen = struct.pack(">L", len(data))
sock.send(slen + data)
if __name__ == "__main__":
# Setup the handler sending log messages to the server.
logger.configure(handlers=[{"sink": SocketHandler('localhost', 9999)}])
# Proceed with standard logger usage.
logger.info("Sending message from the client")
Make sure that the server is running while the clients are logging messages, and note that they must communicate on the same port.
Another example, when using Gunicorn and FastAPI you should add the previously defined ``SocketHandler`` to each of the running workers, possibly like so::
from contextlib import asynccontextmanager
from fastapi import FastAPI
from loguru import logger
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Setup the server instance (executed once for each worker)."""
logger.configure(handlers=[{"sink": SocketHandler("localhost", 9999)}])
logger.debug("Worker is initializing")
yield
app = FastAPI(lifespan=lifespan)
When sharing the logger between processes is not technically possible, using a server handling TCP requests is the most reliable way of guaranteeing the integrity of logged messages.
Using ZMQ to send and receive log messages
------------------------------------------
Third-party libraries like |zmq|_ can be leveraged to exchange messages between multiple processes. Here is an example of a basic server and client:
.. code::
# client.py
import zmq
from zmq.log.handlers import PUBHandler
from logging import Formatter
from loguru import logger
socket = zmq.Context().socket(zmq.PUB)
socket.connect("tcp://127.0.0.1:12345")
handler = PUBHandler(socket)
handler.setFormatter(Formatter("%(message)s"))
logger.add(handler)
logger.info("Logging from client")
.. code::
# server.py
import sys
import zmq
from loguru import logger
socket = zmq.Context().socket(zmq.SUB)
socket.bind("tcp://127.0.0.1:12345")
socket.subscribe("")
logger.configure(handlers=[{"sink": sys.stderr, "format": "{message}"}])
while True:
_, message = socket.recv_multipart()
logger.info(message.decode("utf8").strip())
Resolving ``UnicodeEncodeError`` and other encoding issues
----------------------------------------------------------
When you write a log message, the handler may need to encode the received `unicode`_ string to a specific sequence of bytes. The ``encoding`` used to perform this operation varies depending on the sink type and your environment. Problem may occur if you try to write a character which is not supported by the handler ``encoding``. In such case, it's likely that Python will raise an |UnicodeEncodeError|.
For example, this may happen while printing to the terminal::
print("天")
# UnicodeEncodeError: 'charmap' codec can't encode character '\u5929' in position 0: character maps to <undefined>
A similar error may occur while writing to a file which has not been opened using an appropriate encoding. Most common problem happen while logging to standard output or to a file on Windows. So, how to avoid such error? Simply by properly configuring your handler so that it can process any kind of unicode string.
If you are encountering this error while logging to ``stdout``, you have several options:
* Use |sys.stderr| instead of |sys.stdout| (the former will escape faulty characters rather than raising exception)
* Set the :envvar:`PYTHONIOENCODING` environment variable to ``utf-8``
* Call |sys.stdout.reconfigure| with ``encoding='utf-8'`` and / or ``errors='backslashreplace'``
If you are using a file sink, you can configure the ``errors`` or ``encoding`` parameter while adding the handler like ``logger.add("file.log", encoding="utf8")`` for example. All additional ``**kwargs`` argument are passed to the built-in |open| function.
For other types of handlers, you have to check if there is a way to parametrize encoding or fallback policy.
Logging entry and exit of functions with a decorator
----------------------------------------------------
In some cases, it might be useful to log entry and exit values of a function. Although Loguru doesn't provide such feature out of the box, it can be easily implemented by using Python decorators::
import functools
from loguru import logger
def logger_wraps(*, entry=True, exit=True, level="DEBUG"):
def wrapper(func):
name = func.__name__
@functools.wraps(func)
def wrapped(*args, **kwargs):
logger_ = logger.opt(depth=1)
if entry:
logger_.log(level, "Entering '{}' (args={}, kwargs={})", name, args, kwargs)
result = func(*args, **kwargs)
if exit:
logger_.log(level, "Exiting '{}' (result={})", name, result)
return result
return wrapped
return wrapper
You could then use it like this::
@logger_wraps()
def foo(a, b, c):
logger.info("Inside the function")
return a * b * c
def bar():
foo(2, 4, c=8)
bar()
Which would result in:
.. code-block:: none
2019-04-07 11:08:44.198 | DEBUG | __main__:bar:30 - Entering 'foo' (args=(2, 4), kwargs={'c': 8})
2019-04-07 11:08:44.198 | INFO | __main__:foo:26 - Inside the function
2019-04-07 11:08:44.198 | DEBUG | __main__:bar:30 - Exiting 'foo' (result=64)
Here is another simple example to record timing of a function::
def timeit(func):
def wrapped(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
logger.debug("Function '{}' executed in {:f} s", func.__name__, end - start)
return result
return wrapped
Finally, here is an example of a generic wrapper that combines a success message with error handling during function execution::
def with_logging(func):
@functools.wraps(func)
@logger.catch(message=f"Error in {func.__name__}")
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
logger.success(f"Successfully completed {func.__name__}")
return result
return wrapper
@with_logging
def may_fail(x):
if x < 0:
raise ValueError("Negative value!")
return x * 2
may_fail(10) # Should log success
may_fail(-5) # Should log an error
Using logging function based on custom added levels
---------------------------------------------------
After adding a new level, it's habitually used with the |log| function::
logger.level("foobar", no=33, icon="🤖", color="<blue>")
logger.log("foobar", "A message")
For convenience, one can assign a new logging function which automatically uses the custom added level::
from functools import partialmethod
logger.__class__.foobar = partialmethod(logger.__class__.log, "foobar")
logger.foobar("A message")
The new method need to be added only once and will be usable across all your files importing the ``logger``. Assigning the method to ``logger.__class__`` rather than ``logger`` directly ensures that it stays available even after calling ``logger.bind()``, ``logger.patch()`` and ``logger.opt()`` (because these functions return a new ``logger`` instance).
Setting permissions on created log files
----------------------------------------
To set desired permissions on created log files, use the ``opener`` argument to pass in a custom opener with permissions octal::
def opener(file, flags):
return os.open(file, flags, 0o600) # read/write by owner only
logger.add("foo.log", rotation="100 kB", opener=opener)
When using an opener argument, all created log files including ones created during rotation will use the initially provided opener.
Note that the provided mode will be masked out by the OS `umask <https://en.wikipedia.org/wiki/Umask>`_ value (describing which bits are *not* to be set when creating a file or directory). This value is conventionally equals to ``0o022``, which means specifying a ``0o666`` mode will result in a ``0o666 - 0o022 = 0o644`` file permission in this case (which is actually the default). It is possible to change the umask value by first calling |os.umask|, but this needs to be done with careful consideration, as it changes the value globally and can cause security issues.
Preserving an ``opt()`` parameter for the whole module
------------------------------------------------------
Supposing you wish to color each of your log messages without having to call ``logger.opt(colors=True)`` every time, you can add this at the very beginning of your module::
logger = logger.opt(colors=True)
logger.info("It <green>works</>!")
However, it should be noted that it's not possible to chain |opt| calls, using this method again will reset the ``colors`` option to its default value (which is ``False``). For this reason, it is also necessary to patch the |opt| method so that all subsequent calls continue to use the desired value::
from functools import partial
logger = logger.opt(colors=True)
logger.opt = partial(logger.opt, colors=True)
logger.opt(raw=True).info("It <green>still</> works!\n")
Serializing log messages using a custom function
------------------------------------------------
Each handler added with ``serialize=True`` will create messages by converting the logging record to a valid JSON string. Depending on the sink for which the messages are intended, it may be useful to make changes to the generated string. Instead of using the ``serialize`` parameter, you can implement your own serialization function and use it directly in your sink::
def serialize(record):
subset = {"timestamp": record["time"].timestamp(), "message": record["message"]}
return json.dumps(subset)
def sink(message):
serialized = serialize(message.record)
print(serialized)
logger.add(sink)
If you need to send structured logs to a file (or any kind of sink in general), a similar result can be obtained by using a custom ``format`` function::
def formatter(record):
# Note this function returns the string to be formatted, not the actual message to be logged
record["extra"]["serialized"] = serialize(record)
return "{extra[serialized]}\n"
logger.add("file.log", format=formatter)
You can also use |patch| for this, so the serialization function will be called only once in case you want to use it in multiple sinks::
def patching(record):
record["extra"]["serialized"] = serialize(record)
logger = logger.patch(patching)
# Note that if "format" is not a function, possible exception will be appended to the message
logger.add(sys.stderr, format="{extra[serialized]}")
logger.add("file.log", format="{extra[serialized]}")
Adapting colors and format of logged messages dynamically
---------------------------------------------------------
It is possible to customize the colors of your logs thanks to several :ref:`markup tags <color>`. Those are used to configure the ``format`` of your handler. By creating a appropriate formatting function, you can easily define colors depending on the logged message.
For example, if you want to associate each module with a unique color::
from collections import defaultdict
from random import choice
colors = ["blue", "cyan", "green", "magenta", "red", "yellow"]
color_per_module = defaultdict(lambda: choice(colors))
def formatter(record):
color_tag = color_per_module[record["name"]]
return "<" + color_tag + ">[{name}]</> <bold>{message}</>\n{exception}"
logger.add(sys.stderr, format=formatter)
If you need to dynamically colorize the ``record["message"]``, make sure that the color tags appear in the returned format instead of modifying the message::
def rainbow(text):
colors = ["red", "yellow", "green", "cyan", "blue", "magenta"]
chars = ("<{}>{}</>".format(colors[i % len(colors)], c) for i, c in enumerate(text))
return "".join(chars)
def formatter(record):
rainbow_message = rainbow(record["message"])
# Prevent '{}' in message (if any) to be incorrectly parsed during formatting
escaped = rainbow_message.replace("{", "{{").replace("}", "}}")
return "<b>{time}</> " + escaped + "\n{exception}"
logger.add(sys.stderr, format=formatter)
Dynamically formatting messages to properly align values with padding
---------------------------------------------------------------------
The default formatter is unable to vertically align log messages because the length of ``{name}``, ``{function}`` and ``{line}`` are not fixed.
One workaround consists of using padding with some maximum value that should suffice most of the time. For this purpose, you can use Python's string formatting directives, like in this example::
fmt = "{time} | {level: <8} | {name: ^15} | {function: ^15} | {line: >3} | {message}"
logger.add(sys.stderr, format=fmt)
Here, ``<``, ``^`` and ``>`` will left, center, and right-align the respective keys, and pad them to a maximum length.
Other solutions are possible by using a formatting function or class. For example, it is possible to dynamically adjust the padding length based on previously encountered values::
class Formatter:
def __init__(self):
self.padding = 0
self.fmt = "{time} | {level: <8} | {name}:{function}:{line}{extra[padding]} | {message}\n{exception}"
def format(self, record):
length = len("{name}:{function}:{line}".format(**record))
self.padding = max(self.padding, length)
record["extra"]["padding"] = " " * (self.padding - length)
return self.fmt
formatter = Formatter()
logger.remove()
logger.add(sys.stderr, format=formatter.format)
Customizing the formatting of exceptions
----------------------------------------
Loguru will automatically add the traceback of occurring exception while using ``logger.exception()`` or ``logger.opt(exception=True)``::
def inverse(x):
try:
1 / x
except ZeroDivisionError:
logger.exception("Oups...")
if __name__ == "__main__":
inverse(0)
.. code-block:: none
2019-11-15 10:01:13.703 | ERROR | __main__:inverse:8 - Oups...
Traceback (most recent call last):
File "foo.py", line 6, in inverse
1 / x
ZeroDivisionError: division by zero
If the handler is added with ``backtrace=True``, the traceback is extended to see where the exception came from:
.. code-block:: none
2019-11-15 10:11:32.829 | ERROR | __main__:inverse:8 - Oups...
Traceback (most recent call last):
File "foo.py", line 16, in <module>
inverse(0)
> File "foo.py", line 6, in inverse
1 / x
ZeroDivisionError: division by zero
If the handler is added with ``diagnose=True``, then the traceback is annotated to see what caused the problem:
.. code-block:: none
Traceback (most recent call last):
File "foo.py", line 6, in inverse
1 / x
└ 0
ZeroDivisionError: division by zero
It is possible to further personalize the formatting of exception by adding an handler with a custom ``format`` function. For example, supposing you want to format errors using the |stackprinter|_ library::
import stackprinter
def format(record):
format_ = "{time} {message}\n"
if record["exception"] is not None:
record["extra"]["stack"] = stackprinter.format(record["exception"])
format_ += "{extra[stack]}\n"
return format_
logger.add(sys.stderr, format=format)
.. code-block:: none
2019-11-15T10:46:18.059964+0100 Oups...
File foo.py, line 17, in inverse
15 def inverse(x):
16 try:
--> 17 1 / x
18 except ZeroDivisionError:
..................................................
x = 0
..................................................
ZeroDivisionError: division by zero
Displaying a stacktrace without using the error context
-------------------------------------------------------
It may be useful in some cases to display the traceback at the time your message is logged, while no exceptions have been raised. Although this feature is not built-in into Loguru as it is more related to debugging than logging, it is possible to |patch| your logger and then display the stacktrace as needed (using the |traceback| module)::
import traceback
def add_traceback(record):
extra = record["extra"]
if extra.get("with_traceback", False):
extra["traceback"] = "\n" + "".join(traceback.format_stack())
else:
extra["traceback"] = ""
logger = logger.patch(add_traceback)
logger.add(sys.stderr, format="{time} - {message}{extra[traceback]}")
logger.info("No traceback")
logger.bind(with_traceback=True).info("With traceback")
Here is another example that demonstrates how to prefix the logged message with the full call stack::
import traceback
from itertools import takewhile
def tracing_formatter(record):
# Filter out frames coming from Loguru internals
frames = takewhile(lambda f: "/loguru/" not in f.filename, traceback.extract_stack())
stack = " > ".join("{}:{}:{}".format(f.filename, f.name, f.lineno) for f in frames)
record["extra"]["stack"] = stack
return "{level} | {extra[stack]} - {message}\n{exception}"
def foo():
logger.info("Deep call")
def bar():
foo()
logger.remove()
logger.add(sys.stderr, format=tracing_formatter)
bar()
# Output: "INFO | script.py:<module>:23 > script.py:bar:18 > script.py:foo:15 - Deep call"
Manipulating newline terminator to write multiple logs on the same line
-----------------------------------------------------------------------
You can temporarily log a message on a continuous line by combining the use of |bind|, |opt| and a custom ``format`` function. This is especially useful if you want to illustrate a step-by-step process in progress, for example::
def formatter(record):
end = record["extra"].get("end", "\n")
return "[{time}] {message}" + end + "{exception}"
logger.add(sys.stderr, format=formatter)
logger.add("foo.log", mode="w")
logger.bind(end="").debug("Progress: ")
for _ in range(5):
logger.opt(raw=True).debug(".")
logger.opt(raw=True).debug("\n")
logger.info("Done")
.. code-block:: none
[2020-03-26T22:47:01.708016+0100] Progress: .....
[2020-03-26T22:47:01.709031+0100] Done
Note, however, that you may encounter difficulties depending on the sinks you use. Logging is not always appropriate for this type of end-user message.
Capturing standard ``stdout``, ``stderr`` and ``warnings``
----------------------------------------------------------
The use of logging should be privileged over |print|, yet, it may happen that you don't have plain control over code executed in your application. If you wish to capture standard output, you can suppress |sys.stdout| (and |sys.stderr|) with a custom stream object using |contextlib.redirect_stdout|. You have to take care of first removing the default handler, and not adding a new stdout sink once redirected or that would cause dead lock (you may use |sys.__stdout__| instead)::
import contextlib
import sys
from loguru import logger
class StreamToLogger:
def __init__(self, level="INFO"):
self._level = level
def write(self, buffer):
for line in buffer.rstrip().splitlines():
logger.opt(depth=1).log(self._level, line.rstrip())
def flush(self):
pass
logger.remove()
logger.add(sys.__stdout__)
stream = StreamToLogger()
with contextlib.redirect_stdout(stream):
print("Standard output is sent to added handlers.")
You may also capture warnings emitted by your application by replacing |warnings.showwarning|::
import warnings
from loguru import logger
showwarning_ = warnings.showwarning
def showwarning(message, *args, **kwargs):
logger.opt(depth=2).warning(message)
showwarning_(message, *args, **kwargs)
warnings.showwarning = showwarning
Alternatively, if you want to emit warnings based on logged messages, you can simply use |warnings.warn| as a sink::
logger.add(warnings.warn, format="{message}", filter=lambda record: record["level"].name == "WARNING")
Circumventing modules whose ``__name__`` value is absent
--------------------------------------------------------
Loguru makes use of the global variable ``__name__`` to determine from where the logged message is coming from. However, it may happen in very specific situation (like some Dask distributed environment) that this value is not set. In such case, Loguru will use ``None`` to make up for the lack of the value. This implies that if you want to |disable| messages coming from such special module, you have to explicitly call ``logger.disable(None)``.
Similar considerations should be taken into account while dealing with the ``filter`` attribute. As ``__name__`` is missing, Loguru will assign the ``None`` value to the ``record["name"]`` entry. It also means that once formatted in your log messages, the ``{name}`` token will be equals to ``"None"``. This can be worked around by manually overriding the ``record["name"]`` value using |patch| from inside the faulty module::
# If Loguru fails to retrieve the proper "name" value, assign it manually
logger = logger.patch(lambda record: record.update(name="my_module"))
You probably should not worry about all of this except if you noticed that your code is subject to this behavior.
Interoperability with ``tqdm`` iterations
-----------------------------------------
Trying to use the Loguru's ``logger`` during an iteration wrapped by the ``tqdm`` library may disturb the displayed progress bar. As a workaround, one can use the ``tqdm.write()`` function instead of writings logs directly to ``sys.stderr``::
import time
from loguru import logger
from tqdm import tqdm
logger.remove()
logger.add(lambda msg: tqdm.write(msg, end=""), colorize=True)
logger.info("Initializing")
for x in tqdm(range(100)):
logger.info("Iterating #{}", x)
time.sleep(0.1)
You may encounter problems with colorization of your logs after importing ``tqdm`` using Spyder on Windows. This issue is discussed in `GH#132`_. You can easily circumvent the problem by calling ``colorama.deinit()`` right after your import.
Using Loguru's ``logger`` within a Cython module
------------------------------------------------
Loguru and Cython do not interoperate very well. This is because Loguru (and logging generally) heavily relies on Python stack frames while Cython, being an alternative Python implementation, try to get rid of these frames for optimization reasons.
Calling the ``logger`` from code compiled with Cython may result in "incomplete" logs (missing call context):
.. code-block:: none
2024-11-26 15:58:48.985 | INFO | None:<unknown>:0 - Message from Cython!
This happens when Loguru tries to access a stack frame which has been suppressed by Cython. In such a case, there is no way for Loguru to retrieve contextual information of the logged message.
You can update the default ``format`` of your handlers and omit the uninteresting fields. You can also tries to |patch| the ``logger`` to manually add information you may know about the caller, for example::
logger = logger.patch(lambda record: record.update(name="my_cython_module"))
Note that the ``"name"`` attribute of the log record is set to ``None`` when the frame is unavailable.
.. _creating-independent-loggers:
Creating independent loggers with separate set of handlers
----------------------------------------------------------
Loguru is fundamentally designed to be usable with exactly one global ``logger`` object dispatching logging messages to the configured handlers. In some circumstances, it may be useful to have specific messages logged to specific handlers.
For example, supposing you want to split your logs in two files based on an arbitrary identifier, you can achieve that by combining |bind| and ``filter``::
from loguru import logger
def task_A():
logger_a = logger.bind(task="A")
logger_a.info("Starting task A")
do_something()
logger_a.success("End of task A")
def task_B():
logger_b = logger.bind(task="B")
logger_b.info("Starting task B")
do_something_else()
logger_b.success("End of task B")
logger.add("file_A.log", filter=lambda record: record["extra"]["task"] == "A")
logger.add("file_B.log", filter=lambda record: record["extra"]["task"] == "B")
task_A()
task_B()
That way, ``"file_A.log"`` and ``"file_B.log"`` will only contains logs from respectively the ``task_A()`` and ``task_B()`` function.
Now, supposing that you have a lot of these tasks. It may be a bit cumbersome to configure every handlers like this. Most importantly, it may unnecessarily slow down your application as each log will need to be checked by the ``filter`` function of each handler. In such case, it is recommended to rely on the |copy.deepcopy| built-in method that will create an independent ``logger`` object. If you add a handler to a deep copied ``logger``, it will not be shared with others functions using the original ``logger``::
import copy
from loguru import logger
def task(task_id, logger):
logger.info("Starting task {}", task_id)
do_something(task_id)
logger.success("End of task {}", task_id)
logger.remove()
for task_id in ["A", "B", "C", "D", "E"]:
logger_ = copy.deepcopy(logger)
logger_.add("file_%s.log" % task_id)
task(task_id, logger_)
Note that you may encounter errors if you try to copy a ``logger`` to which non-picklable handlers have been added. For this reason, it is generally advised to remove all handlers before calling ``copy.deepcopy(logger)``.
.. _multiprocessing-compatibility:
Compatibility with ``multiprocessing`` using ``enqueue`` argument
-----------------------------------------------------------------
On Linux, thanks to |os.fork| there is no pitfall while using the ``logger`` inside another process started by the |multiprocessing| module. The child process will automatically inherit added handlers, the ``enqueue=True`` parameter is optional but is recommended as it would avoid concurrent access of your sink::
# Linux implementation
import multiprocessing
from loguru import logger
def my_process():
logger.info("Executing function in child process")
logger.complete()
if __name__ == "__main__":
logger.add("file.log", enqueue=True)
process = multiprocessing.Process(target=my_process)
process.start()
process.join()
logger.info("Done")
Things get a little more complicated on Windows. Indeed, this operating system does not support forking, so Python has to use an alternative method to create sub-processes called "spawning". This procedure requires the whole file where the child process is created to be reloaded from scratch. This does not interoperate very well with Loguru, causing handlers to be added twice without any synchronization or, on the contrary, not being added at all (depending on ``add()`` and ``remove()`` being called inside or outside the ``__main__`` branch). For this reason, the ``logger`` object need to be explicitly passed as an initializer argument of your child process::
# Windows implementation
import multiprocessing
from loguru import logger
def my_process(logger_):
logger_.info("Executing function in child process")
logger_.complete()
if __name__ == "__main__":
logger.remove() # Default "sys.stderr" sink is not picklable
logger.add("file.log", enqueue=True)
process = multiprocessing.Process(target=my_process, args=(logger, ))
process.start()
process.join()
logger.info("Done")
Windows requires the added sinks to be picklable or otherwise will raise an error while creating the child process. Many stream objects like standard output and file descriptors are not picklable. In such case, the ``enqueue=True`` argument is required as it will allow the child process to only inherit the queue object where logs are sent.
The |multiprocessing| library is also commonly used to start a pool of workers using for example |Pool.map| or |Pool.apply|. Again, it will work flawlessly on Linux, but it will require some tinkering on Windows. You will probably not be able to pass the ``logger`` as an argument for your worker functions because it needs to be picklable, but although handlers added using ``enqueue=True`` are "inheritable", they are not "picklable". Instead, you will need to make use of the ``initializer`` and ``initargs`` parameters while creating the |Pool| object in a way allowing your workers to access the shared ``logger``. You can either assign it to a class attribute or override the global logger of your child processes:
.. code::
# workers_a.py
class Worker:
_logger = None
@staticmethod
def set_logger(logger_):
Worker._logger = logger_
def work(self, x):
self._logger.info("Square rooting {}", x)
return x**0.5
.. code::
# workers_b.py
from loguru import logger
def set_logger(logger_):
global logger
logger = logger_
def work(x):
logger.info("Square rooting {}", x)
return x**0.5
.. code::
# main.py
from multiprocessing import Pool
from loguru import logger
import workers_a
import workers_b
if __name__ == "__main__":
logger.remove()
logger.add("file.log", enqueue=True)
worker = workers_a.Worker()
with Pool(4, initializer=worker.set_logger, initargs=(logger, )) as pool:
results = pool.map(worker.work, [1, 10, 100])
with Pool(4, initializer=workers_b.set_logger, initargs=(logger, )) as pool:
results = pool.map(workers_b.work, [1, 10, 100])
logger.info("Done")
Independently of the operating system, note that the process in which a handler is added with ``enqueue=True`` is in charge of the queue internally used. This means that you should avoid to ``.remove()`` such handler from the parent process is any child is likely to continue using it. More importantly, note that a |Thread| is started internally to consume the queue. Therefore, it is recommended to call |complete| before leaving |Process| to make sure the queue is left in a stable state.
Another thing to keep in mind when dealing with multiprocessing is the fact that handlers created with ``enqueue=True`` create a queue internally in the default multiprocessing context. If they are passed through to a subprocesses instantiated within a different context (e.g. with ``multiprocessing.get_context("spawn")`` on linux, where the default context is ``"fork"``) it will most likely result in crashing the subprocess. This is also noted in the `python multiprocessing docs <https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods>`_. To prevent any problems, you should specify the context to be used by Loguru while adding the handler. This can be done by passing the ``context`` argument to the ``add()`` method::
import multiprocessing
from loguru import logger
import workers_a
if __name__ == "__main__":
context = multiprocessing.get_context("spawn")
logger.remove()
logger.add("file.log", enqueue=True, context=context)
worker = workers_a.Worker()
with context.Pool(4, initializer=worker.set_logger, initargs=(logger, )) as pool:
results = pool.map(worker.work, [1, 10, 100])
.. _recipes-testing:
Unit testing logs emitted by Loguru
-----------------------------------
When migrating an existing project from standard :mod:`logging`, it can be useful to migrate your existing test cases too. In particular, you may want to:
- Migrate ``assertLogs()`` from :mod:`unittest` to Loguru: :ref:`migration-assert-logs`
- Migrate ``caplog`` from |pytest|_ to Loguru: :ref:`migration-caplog`
Alternatively, logging calls can be tested using |logot|_, a high-level log testing library with built-in support for Loguru::
from logot import Logot, logged
def test_something(logot: Logot) -> None:
do_something()
logot.assert_logged(logged.info("Something was done"))
Enable Loguru log capture in your |pytest|_ configuration:
.. code:: toml
[tool.pytest.ini_options]
logot_capturer = "logot.loguru.LoguruCapturer"
.. seealso::
See `using logot with Loguru <https://logot.readthedocs.io/latest/integrations/loguru.html>`_ for more information
about `configuring pytest <https://logot.readthedocs.io/latest/integrations/loguru.html#enabling-for-pytest>`_
and `configuring unittest <https://logot.readthedocs.io/latest/integrations/loguru.html#enabling-for-unittest>`_.
.. _add_opentelemetry_traces_id:
Add OpenTelemetry ``trace_id`` and ``span_id`` to logs
------------------------------------------------------
There is no official implementation of OpenTelemetry for Loguru as of now, but one frequent task when integrating Loguru and OpenTelemetry
is adding the trace context to the logs to create a correlation between them. The following example uses |patch| to automatically inject OpenTelemetry trace context into every log record::
from loguru import logger
import sys
from opentelemetry.trace import (
INVALID_SPAN,
INVALID_SPAN_CONTEXT,
get_current_span,
get_tracer_provider,
)
def instrument_loguru():
provider = get_tracer_provider()
service_name = None
def add_trace_context(record):
record["extra"]["otelSpanID"] = "0"
record["extra"]["otelTraceID"] = "0"
record["extra"]["otelTraceSampled"] = False
nonlocal service_name
if service_name is None:
resource = getattr(provider, "resource", None)
if resource:
service_name = resource.attributes.get("service.name") or ""
else:
service_name = ""
record["extra"]["otelServiceName"] = service_name
span = get_current_span()
if span != INVALID_SPAN:
ctx = span.get_span_context()
if ctx != INVALID_SPAN_CONTEXT:
record["extra"]["otelSpanID"] = format(ctx.span_id, "016x")
record["extra"]["otelTraceID"] = format(ctx.trace_id, "032x")
record["extra"]["otelTraceSampled"] = ctx.trace_flags.sampled
logger.configure(patcher=add_trace_context)
instrument_loguru()
logger.remove()
format_ = "{time:YYYY-MM-DD HH:MM:SS.sss} {level} [{name}] [{file}:{line} [trace_id={extra[otelTraceID]} span_id={extra[otelSpanID]} resource.service.name={extra[otelServiceName]} trace_sampled={extra[otelTraceSampled]}] - {message}"
logger.add(sys.stderr, format=format_)
logger.info("This is an info message")
Alternatively, a custom formatter can be added to be able to save ``otelSpanID`` and ``otelTraceID`` in the root of the JSON log.
================================================
FILE: docs/resources/troubleshooting.rst
================================================
Frequently Asked Questions and Troubleshooting Tips for Loguru
==============================================================
.. highlight:: python3
.. |sys.stdout| replace:: :data:`sys.stdout`
.. |sys.stderr| replace:: :data:`sys.stderr`
.. |str.format| replace:: :meth:`str.format()`
.. |isatty| replace:: :meth:`~io.IOBase.isatty`
.. |IOBase.close| replace:: :meth:`~io.IOBase.close`
.. |Logger| replace:: :class:`~loguru._logger.Logger`
.. |add| replace:: :meth:`~loguru._logger.Logger.add()`
.. |remove| replace:: :meth:`~loguru._logger.Logger.remove()`
.. |bind| replace:: :meth:`~loguru._logger.Logger.bind()`
.. |opt| replace:: :meth:`~loguru._logger.Logger.opt()`
.. |patch| replace:: :meth:`~loguru._logger.Logger.patch()`
.. |log| replace:: :meth:`~loguru._logger.Logger.log()`
.. |colorama| replace:: ``colorama``
.. _colorama: https://github.com/tartley/colorama
.. |if-name-equals-main| replace:: ``if __name__ == "__main__":``
.. _if-name-equals-main: https://docs.python.org/3/library/__main__.html#idiomatic-usage
.. |the-no-color-environment-variable| replace:: the ``NO_COLOR`` environment variable
.. _the-no-color-environment-variable: https://no-color.org/
.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
How do I create and configure a logger?
---------------------------------------
Loguru differs from standard logging as you don't need to create a logger. It is directly provided by Loguru, and you should just import it::
from loguru import logger
logger.info("Hello, World!")
This |Logger| object is unique and shared across all modules of your application. Import it into every file where you need to use it. It acts as a basic facade interface around a list of handlers. These handlers are responsible for receiving log messages, formatting them, and logging them to one or more desired destinations (file, console, etc.).
When you first import Loguru's logger, it comes pre-configured with a default handler that displays your logs on the standard error output (|sys.stderr|). However, you can easily change the logger's configuration to suit your needs. First, use |remove| to discard the default handler. Then, use |add| to register one or more handlers that will log messages to the desired destinations. For example::
logger.remove() # Remove the default handler.
logger.add(sys.stderr, format="{time} - {level} - {message}") # Log to console with custom format.
logger.add("file.log", level="INFO", rotation="500 MB") # Also log to a file, rotating every 500 MB.
The logger should be configured only once, at the entry point of your application (typically within a |if-name-equals-main|_ block). Other modules in your application will automatically inherit this configuration by simply importing Loguru's global ``logger``.
.. seealso::
:ref:`Configuring Loguru to be used by a library or an application <configuring-loguru-as-lib-or-app>`
Why are my logs duplicated in the output?
-----------------------------------------
Remember that the initial ``logger`` has a default handler for convenience. If you plan to change the logging configuration, make sure to |remove| this default handler before to |add| a new one. Otherwise, messages will be duplicated because they will be sent to both the default handler and your new handler::
# Replace the default handler with a new one.
logger.remove()
logger.add(sys.stderr, format="{time} - {level} - {message}")
Additionally, since there is a single ``logger`` shared across all modules in your application, you should configure it in one place only. Handlers will be added as many times as ``logger.add()`` is called, so be careful not to reconfigure it multiple times.
In particular when using ``multiprocessing`` (either directly or indirectly through a web framework, for instance), ensure that the ``logger`` configuration is guarded by an if |if-name-equals-main|_ block. Otherwise, each spawned child process will re-execute the configuration code. This can result in duplicated logs or unexpected configurations. See :ref:`this section of the documentation <multiprocessing-compatibility>` for details.
Finally, don't forget that the ``level`` argument of |add| defines a minimum threshold, not an exact filtering mechanism. It is generally a mistake to add two handlers with the same sink, as it will cause duplication unless they are configured with mutually exclusive ``filter`` functions. For example::
def is_debug(record):
return record["level"].no <= 10
logger.add(sys.stderr, level="DEBUG", format="{time} - {name} - {message}", filter=is_debug)
logger.add(sys.stderr, level="INFO", format="{message}", filter=lambda r: not is_debug(r))
How do I set the logging level?
-------------------------------
The :ref:`logging levels <levels>` allow filtering messages based on their importance. It is a minimum threshold above which messages are logged (or ignored otherwise). This makes it possible, for example, to adjust the verbosity of logs depending on the execution environment (development or production).
The |Logger| itself is not associated with any specific level. Instead, it is the level of each handler that individually determines whether a message is logged or not. This level is defined when configuring the handler and adding it to the logger using the ``level`` argument of the |add| method::
logger.add(sys.stdout, level="WARNING") # Log only messages with level "WARNING" or higher.
logger.debug("Some debug message") # Will be ignored.
logger.error("Some error message") # Will be displayed.
It is not possible to change the level of an existing handler. If you need to modify the logging level, you can |remove| the existing handler and |add| a new one with the desired level::
logger.remove() # Remove the default handler.
logger.add(sys.stderr, level="INFO")
By default, the level of each handler is ``"DEBUG"``. You can adjust this value :ref:`using environment variables <env>`:
.. code:: text
export LOGURU_LEVEL="INFO" # On Linux or macOS.
setx LOGURU_LEVEL "INFO" # On Windows.
Note that this only affects handlers that do not explicitly configure their level.
.. seealso::
:ref:`Changing the level of an existing handler <changing-level-of-existing-handler>`
.. _anonymous_levels:
Why isn't the level name shown when using an integer or a built-in level?
-------------------------------------------------------------------------
You may have noticed that when a numeric value is passed to the |log| function, or equivalently when a level from the standard ``logging`` library is used, it appears in the logs as "Level *NUM*" rather than the expected level name::
import logging
from loguru import logger
logger.log(logging.INFO, "This is an info message.")
# Output: 2025-11-02 12:40:37.266 +01:00 | Level 20 | __main__:<module>:4 - This is an info message.
This behavior is normal and stems from the fact that, unlike the standard logging library, Loguru levels are identified exclusively by their name (and not by their severity).
Indeed, it is entirely possible to define different logging levels that share the same severity number. In such cases, however, it becomes impossible to identify a level based on severity alone. To avoid any ambiguity, Loguru maps each level name with its severity number, but not the opposite.
Consequently, if an integer is provided to |log|, the level is interpreted as an anonymous one and displayed as such.
How do I customize the log format and re-use the default one?
-------------------------------------------------------------
The log format must be defined using the ``format`` argument of the |add| method::
logger.add(sys.stderr, format="{time} - {level} - {message}")
Refer to :ref:`this section of the documentation <record>` to learn about the different formatting variables available. You can also use :ref:`color tags <color>`::
logger.add(sys.stderr, format="<green>{time}</> - {level} - <lvl>{message}</>")
For advanced configuration, the ``format`` argument also accepts a function, allowing you to dynamically generate the desired format. Be aware that in this case, you have to explicitly include the line ending and exception field (since you gain full control over the formatting, while ``"\n{exception}"`` is added automatically when the ``format`` is a string). For example, to include the thread identifier but only for error messages and above::
def custom_formatter(record):
if record["level"].no >= 40:
return "<green>{time}</> - {level} - <red>{thread}</> - <lvl>{message}</>\n{exception}"
else:
return "<green>{time}</> - {level} - <lvl>{message}</lvl>\n{exception}"
logger.add(sys.stderr, format=custom_formatter)
Finally, note that accessing the default log format is not directly possible, as it would only be useful in a very limited number of cases. Instead, you need to explicitly redefine your desired format. To quickly copy-paste the default logging format, check out the ``LOGURU_FORMAT`` variable `in the source code <https://github.com/Delgan/loguru/blob/master/loguru/_defaults.py>`_.
Why are my logs not colored?
----------------------------
Log colors are configured using :ref:`special tags <color>` in the ``format`` of the handlers. If you use a custom ``format``, make sure that these tags are included, for example::
logger.add(sys.stderr, format="<green>{time}</green> | <level>{message}</level>")
When adding a handler with ``colorize=None`` (the default), Loguru tries to automatically detect whether the added sink (such as ``sys.stderr`` in the above example) supports colors. If it's not the case, color tags will be stripped. Otherwise, they'll be converted to `ANSI escape sequences`_.
These sequences are generally only supported within a terminal. Therefore, it is normal that you don't see colors when logs are saved to a text file. Sinks that support colors are usually |sys.stderr| and |sys.stdout|::
logger.add(sys.stderr) # Can be colored.
logger.add("file.log") # Cannot be colored.
When such stream object is used for logging, Loguru will also call |isatty| to determine whether colors should be used. This method notably returns ``False`` if the stream is not connected to a terminal, which would make colorization pointless. For example, redirecting the output of your script to a file will disable colors:
.. code-block:: bash
python my_script.py > output.log # Colors will be disabled.
Additionally, it is not uncommon in some virtual environments for the standard output not to be considered as connected to a terminal, even though you can view the logs' output live without redirection. This is the case, for instance, in some cloud services. Check the value of ``sys.stderr.isatty()`` if you encounter any issues.
Various heuristics assist in determining whether colors should be enabled by default. Specifically, Loguru honors |the-no-color-environment-variable|_ and disables coloring if it is set to any value. Additionally, terminals identified by ``TERM=dumb`` are considered to lack color support.
In any case, you can always control log coloring by explicitly specifying the ``colorize`` argument of the |add| method::
logger.add(sys.stderr, colorize=True) # Force ANSI sequences in output.
Conversely, if raw ANSI sequences such as ``\x1b[31m`` or ``\x1b[0m`` appear in your logs, it certainly means the sink does not support colors, and you should disable them.
Note that on Windows, log coloring is handled using the |colorama|_ library.
Why are my logs not showing up?
-------------------------------
Ensure that you've added at least one sink using |add|. You can get an overview of the configured handlers by simply printing the logger object::
print(logger)
# Output: <loguru.logger handlers=[(id=0, level=10, sink=<stderr>)]>
Check also the logging level: messages below the set level won't appear::
logger.add(sys.stderr, level="INFO")
logger.debug("Some debug message") # Won't be displayed since "DEBUG" is below "INFO".
Why is the captured exception missing from the formatted message?
-----------------------------------------------------------------
When ``logger.exception()`` or ``logger.opt(exception=True)`` is used within an ``except`` clause, Loguru automatically captures the exception information and includes it in :ref:`the logged message <message>`.
The position of the exception in the message is controlled by the ``"{exception}"`` field of the configured log format. By default, when the ``format`` argument of |add| is a string, the ``"{exception}"`` field is automatically appended to the format::
# The "{exception}" placeholder is implicit here (at the end of the format).
log_format = "{time} - {level} - {message}"
logger.add(sys.stderr, format=log_format)
However, when using a custom function to define the format of logs, the user gets complete control over the desired format. This means the ``"{exception}"`` field must be explicitly included::
def custom_formatter(record):
return "{time} - {level} - {message}\n{exception}"
logger.add(sys.stderr, format=custom_formatter)
If the field is missing, the formatted error will not appear in the log message. Always ensure the ``"{exception}"`` placeholder is present in your log format if you want exception details to appear in your logs.
How can I use different loggers in different modules of my application?
-----------------------------------------------------------------------
Since Loguru is designed on the use of a single ``logger``, it is fundamentally not possible to create different loggers for multiple modules. The idea is that modules should simply import the global ``logger`` from ``loguru``, and log differentiation should be handled through handlers (which should only be configured once, at the application's entry point).
Note that is generally possible to identify the origin of a log message via the ``record["name"]`` field in the record dict. This field contains the name of the module that emitted the message. For example, you can use this information to redirect messages based on their origin::
logger.add("my_app.log") # All messages.
logger.add("module_1.log", filter="module_1") # Messages from "module_1" only.
logger.add("module_2.log", filter="module_2") # Messages from "module_2" only.
For more advanced use cases, it is recommended to use the |bind| method, which returns a new instance of the ``logger`` tied to the given value. This allows you to identify logs more precisely::
def is_specific_log(record):
return record["extra"].get("is_specific") is True
logger.add("specific.log", filter=is_specific_log)
logger.add("other.log", filter=lambda r: not is_specific_log(r))
specific_logger = logger.bind(is_specific=True)
specific_logger.info("This message will go to 'specific.log' only.")
logger.info("This message will go to 'other.log' only.")
.. seealso::
:ref:`Creating independent loggers with separate set of handlers <creating-independent-loggers>`
Why are my log files sometimes duplicated or the content trimmed?
-----------------------------------------------------------------
Problem with logging files duplicated or trimmed is generally symptomatic of a configuration issue. More precisely, this can happen if |add| is inadvertently called multiple times with the same file path.
When this happens, the file is opened again by the newly created handler. Consequently, multiple handlers manage and write to the same file concurrently. This is an incorrect situation that inevitably leads to conflicts. If the problem isn't detected, handlers risk overwriting logs over each other, otherwise it can also result in duplicated files at the moment of the rotation.
If you observe such weird behavior, you should review your code carefully to ensure that the same file sink is not being added multiple times. This can occur if ``multiprocessing`` is used incorrectly (see :ref:`this section of the documentation <multiprocessing-compatibility>` for more details). You have to make sure that the logger is not configured repeatedly by different processes, and you should use a |if-name-equals-main|_ guard.
It is also a common issue with web frameworks like Gunicorn and Uvicorn, as they start multiple workers in parallel. In such cases, you need to set up a log server, and configure workers to send messages to it using a socket. Refer to :ref:`Transmitting log messages across network, processes or Gunicorn workers <inter-process-communication>` for details.
Why am I facing errors when I use a custom formatting function?
---------------------------------------------------------------
When using a custom function to define the log format (by passing a callable to the ``format`` argument of |add|), you must ensure that the function returns a string containing the unformatted template and not an already formatted message.
This string should include placeholders for the fields you want to display (such as ``"{time}"``, ``"{level}"``, ``"{message}"``, etc). Later, Loguru will call an equivalent of |str.format| on this string, passing the log record as keyword arguments to replace the placeholders with actual values. If the returned string is already formatted (i.e., if it contains actual values instead of placeholders), some of the values might be interpreted as placeholders and lead to a ``ValueError`` or ``KeyError`` because they don't correspond to any key in the log record.
For example, the following code is incorrect because the function returns a pre-formatted string::
def incorrect_dynamic_format(record):
# This is wrong because it formats the message immediately.
return f"{record['time']} - {record['level']} - {record['message']}"
logger.add(sys.stderr, format=incorrect_dynamic_format)
Instead, the function should return a string with placeholders, like this::
def correct_dynamic_format(record):
# This is correct because it returns a template string.
return "{time} - {level} - {message}\n{exception}"
logger.add(sys.stderr, format=correct_dynamic_format)
Think about this function as a way to dynamically generate the format template, not to format the message itself. Loguru will handle the actual formatting of the log message later, using the template you provide.
If for some reason you need to format the message yourself, you should save the result in a new field of the record's ``"extra"`` dictionary, and then use this new field in the log format. For example::
def custom_format_with_extra(record):
record["extra"]["custom_msg"] = f"{record['time']} - {record['level']} - {record['message']}"
return "{extra[custom_msg]}\n{exception}"
logger.add(sys.stderr, format=custom_format_with_extra)
Finally, note that if your format contains curly braces that are not meant to be placeholders, you must escape them by doubling them::
def custom_json_format(record):
return '{{ "@timestamp": {time:X}, "levelno": "{level.no}", "msg": "{message}" }}'
logger.add(sys.stderr, format=custom_json_format)
Why logging a message with f-string sometimes raises an exception?
------------------------------------------------------------------
When positional or keyword arguments are passed to the logging function, Loguru will integrate them to the message. For example::
logger.info("My name is {name}", name="John")
# Output: [INFO] My name is John
This is actually equivalent to using the |str.format| built-in Python method::
message = "My name is {name}".format(name="John")
logger.info(message)
However, the behavior described above can cause an error if the arguments passed were not intended to be formatted with the message (but rather just captured in the "extra" dict of the log record). This is particularly true if the message contains curly braces. The formatting function will then interpret them as placeholders and attempt to replace them with the passed arguments.
Here are some examples that result in various exceptions:
.. code-block::
# KeyError: 'key1, key2'
logger.warning("Config file missing keys: {key1, key2}", filename="app.cfg")
.. code-block::
# ValueError: Single '{' encountered in format string
logger.info("This is a curly bracket: {", foo="bar")
.. code-block::
# AttributeError: 'dict' object has no attribute 'format'
logger.debug({"key": "value"}, identifier=42)
.. code-block::
# IndexError: Replacement index 0 out of range for positional args tuple
logger.error("Use 'set()' not '{}' for empty set", strictness=9)
It is common to encounter these errors when using f-strings, as this can leads to the creation of a message that already contains curly braces. For example::
data = {"foo": 42}
# Will raise "KeyError" because it's equivalent to:
# logger.info("Processing '{'foo': 42}'", data=data)
logger.info(f"Processing '{data}'", data=data)
Therefore, you must be careful not to inadvertently introduce curly braces into the message. Instead of using an f-string, you can let Loguru handle the formatting::
logger.info("Processing '{data}'", data=data)
You can also use |bind| to add extra information to a message without formatting it::
logger.bind(data=data).info(f"Processing '{data}'")
Finally, you can possibly disable formatting by doubling the curly braces::
logger.info("Curly brackets are {{ and }}", data=data)
How do I fix "ValueError: I/O operation error on closed file"?
--------------------------------------------------------------
This error occurs because the logger is trying to write to a stream object (like ``sys.stderr`` or ``sys.stdout``) that has been closed, which is invalid (see |IOBase.close|).
When stream objects are used as logging sink, Loguru will not close them. This would be very inconvenient and incorrect (as the stream is global, it must remain usable after the sink has been removed). Since Loguru does not close such a stream by itself, this means something else closed the stream while it was still in use by the ``logger``.
This is generally due to some tools or specific environments that take the liberty of replacing ``sys.stdout`` and ``sys.stderr`` with their own stream object. In this way, they can capture what is written to the standard output. This is the case with some libraries, IDEs and cloud platforms.
The problem is that the ``logger`` will use this wrapped stream as well. If the third-party tool happens to clean up and close the stream, then the ``logger`` is left with an unusable sink.
Here is a simplified example to illustrate the issue::
from contextlib import contextmanager
import sys
import io
from loguru import logger
@contextmanager
def redirect_stdout(new_target):
old_target, sys.stdout = sys.stdout, new_target
try:
yield new_target
finally:
sys.stdout = old_target
new_target.close()
if __name__ == "__main__":
logger.remove()
f = io.StringIO()
with redirect_stdout(f):
logger.add(sys.stdout) # Logger is inadvertently configured with wrapped stream.
logger.info("Hello")
output = f.getvalue()
print(f"Captured output: {output}")
# ValueError: I/O operation on closed file.
logger.info("World")
And here is another example causing the same error with Pytest::
import sys
from loguru import logger
logger.remove()
def test_1(capsys):
# Here, "sys.stderr" is actually a mock object due to usage of "capsys" fixture.
logger.add(sys.stderr, catch=False)
logger.info("Test 1")
def test_2():
# After execution of the previous test, the mocked "sys.stderr" was closed by Pytest.
# However, the handler was not removed from the Loguru logger. It'll raise a "ValueError" here.
logger.info("Test 2", catch=False)
What you can possibly do in such a situation:
- identify any tool that could be manipulating ``sys.stdout``, try to call ``print(sys.stdout)`` to see if it's a wrapper object;
- make sure the ``logger`` is always fully re-initialized whenever your code is susceptible to clean up the wrapped ``sys.stdout``;
- configure the ``logger`` with ``logger.add(lambda m: sys.stdout.write(m))`` instead of ``logger.add(sys.stdout)``, so that the stream is dynamically retrieved and therefore not affected by changes.
How do I prevent "RuntimeError" due to "deadlock avoided"?
----------------------------------------------------------
The logging functions are not reentrant. This means you must not use the logger when it's already in use in the same thread. This situation can occur notably if you use the logger inside a sink (which itself is called by the logger). Logically, this would result in an infinite recursive loop. In practice, it would more likely cause your application to hang because logging is protected by an internal lock.
To prevent such problems, there is a mechanism that detects and prevents the logger from being called recursively. This is what might lead to a ``RuntimeError``. When faced with such an error, you need to ensure that the handlers you configure do not internally call the logger. This also applies to the logger from the standard ``logging`` library.
If you cannot prevent the use of the logger inside a handler, you should implement a ``filter`` to avoid recursive calls. For example::
import sys
from loguru import logger
def my_sink(message):
logger.debug("Within my sink")
print(message, end="")
def avoid_recursion(record):
return record["function"] != "my_sink"
if __name__ == "__main__":
logger.remove()
logger.add("file.log")
logger.add(my_sink, filter=avoid_recursion)
logger.info("First message")
logger.debug("Another message")
Why is the source (name, file, function, line) of the log message incorrect or missing?
---------------------------------------------------------------------------------------
In some very specific circumstances, the module name might be ``None`` and the filename and function name might be ``"<unknown>"``.
.. code-block:: none
2024-12-01 16:23:21.769 | INFO | None:<unknown>:0 - Message from unknown source.
Such a situation indicates that the ``logger`` was unable to retrieve the caller's context. In particular, this can happen when Loguru is used with Dask or Cython. In such cases, this behavior is normal, and there is nothing to do unless you wish to implement a custom |patch| function::
logger = logger.patch(lambda record: record.update(name="my_module"))
This issue may also result from improper use of the ``depth`` argument of the |opt| method. Make sure that the value of this argument is correct.
Why can't I access the ``Logger`` class and other types at runtime?
-------------------------------------------------------------------
The ``logger`` object imported from the ``loguru`` library is an instance of the |Logger| class. However, you should not attempt to instantiate a logger yourself. The |Logger| class is not public and will be unusable by your Python application. It is therefore expected that the following code will raise an error::
from loguru import Logger
# Output: ImportError: cannot import name 'Logger' from 'loguru'
It is only possible to use the |Logger| class in the context of type hints. In such cases, no error will be raised. Said otherwise, that means only type checkers can access the |Logger| class. Below is an example of how to use ``Logger`` for typing purposes, but without runtime access::
from __future__ import annotations
import typing
from loguru import logger
if typing.TYPE_CHECKING:
from loguru import Logger
def my_function(logger: Logger):
logger.info("Hello, World!")
If for some reason you need to perform type checking at runtime, you can make a comparison with the type on the ``logger`` instance::
import loguru
import logging
def my_function(logger: loguru.Logger | logging.Logger):
if isinstance(logger, type(loguru.logger)):
logger.info("Hello, {}!", "World")
else:
logger.info("Hello, %s!", "World")
.. seealso::
:ref:`Type hints <type-hints>`
================================================
FILE: docs/resources.rst
================================================
Help & Guides
=============
.. toctree::
resources/migration.rst
resources/troubleshooting.rst
resources/recipes.rst
================================================
FILE: loguru/__init__.py
================================================
"""
The Loguru library provides a pre-instanced logger to facilitate dealing with logging in Python.
Just ``from loguru import logger``.
"""
import atexit as _atexit
import sys as _sys
from . import _defaults
from ._logger import Core as _Core
from ._logger import Logger as _Logger
__version__ = "0.7.3"
__all__ = ["logger"]
logger = _Logger(
core=_Core(),
exception=None,
depth=0,
record=False,
lazy=False,
colors=False,
raw=False,
capture=True,
patchers=[],
extra={},
)
if _defaults.LOGURU_AUTOINIT and _sys.stderr:
logger.add(_sys.stderr)
_atexit.register(logger.remove)
================================================
FILE: loguru/__init__.pyi
================================================
import sys
from asyncio import AbstractEventLoop
from datetime import datetime, time, timedelta
from logging import Handler
from multiprocessing.context import BaseContext
from types import TracebackType
from typing import (
Any,
BinaryIO,
Callable,
Dict,
Generator,
Generic,
List,
NamedTuple,
NewType,
Optional,
Pattern,
Sequence,
TextIO,
Tuple,
Type,
TypeVar,
Union,
overload,
)
if sys.version_info >= (3, 6):
from typing import Awaitable
else:
from typing_extensions import Awaitable
if sys.version_info >= (3, 6):
from os import PathLike
from typing import ContextManager
PathLikeStr = PathLike[str]
else:
from pathlib import PurePath as PathLikeStr
from typing_extensions import ContextManager
if sys.version_info >= (3, 8):
from typing import Protocol, TypedDict
else:
from typing_extensions import Protocol, TypedDict
_T = TypeVar("_T")
_F = TypeVar("_F", bound=Callable[..., Any])
ExcInfo = Tuple[Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]]
class _GeneratorContextManager(ContextManager[_T], Generic[_T]):
def __call__(self, func: _F) -> _F: ...
def __exit__(
self,
type: Optional[Type[BaseException]],
value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> Optional[bool]: ...
Catcher = NewType("Catcher", _GeneratorContextManager[None])
Contextualizer = NewType("Contextualizer", _GeneratorContextManager[None])
AwaitableCompleter = Awaitable[None]
class Level(NamedTuple):
name: str
no: int
color: str
icon: str
class _RecordAttribute:
def __format__(self, spec: str) -> str: ...
class RecordFile(_RecordAttribute):
name: str
path: str
class RecordLevel(_RecordAttribute):
name: str
no: int
icon: str
class RecordThread(_RecordAttribute):
id: int
name: str
class RecordProcess(_RecordAttribute):
id: int
name: str
class RecordException(NamedTuple):
type: Optional[Type[BaseException]]
value: Optional[BaseException]
traceback: Optional[TracebackType]
class Record(TypedDict):
elapsed: timedelta
exception: Optional[RecordException]
extra: Dict[Any, Any]
file: RecordFile
function: str
level: RecordLevel
line: int
message: str
module: str
name: Optional[str]
process: RecordProcess
thread: RecordThread
time: datetime
class Message(str):
record: Record
class Writable(Protocol):
def write(self, message: Message) -> Any: ...
FilterDict = Dict[Optional[str], Union[str, int, bool]]
FilterFunction = Callable[[Record], bool]
FormatFunction = Callable[[Record], str]
PatcherFunction = Callable[[Record], None]
RotationFunction = Callable[[Message, TextIO], bool]
RetentionFunction = Callable[[List[str]], None]
CompressionFunction = Callable[[str], None]
StandardOpener = Callable[[str, int], int]
class BasicHandlerConfig(TypedDict, total=False):
sink: Union[TextIO, Writable, Callable[[Message], None], Handler]
level: Union[str, int]
format: Union[str, FormatFunction]
filter: Optional[Union[str, FilterFunction, FilterDict]]
colorize: Optional[bool]
serialize: bool
backtrace: bool
diagnose: bool
enqueue: bool
catch: bool
class FileHandlerConfig(TypedDict, total=False):
sink: Union[str, PathLikeStr]
level: Union[str, int]
format: Union[str, FormatFunction]
filter: Optional[Union[str, FilterFunction, FilterDict]]
colorize: Optional[bool]
serialize: bool
backtrace: bool
diagnose: bool
enqueue: bool
catch: bool
rotation: Optional[
Union[
str,
int,
time,
timedelta,
RotationFunction,
list[Union[str, int, time, timedelta, RotationFunction]],
]
]
retention: Optional[Union[str, int, timedelta, RetentionFunction]]
compression: Optional[Union[str, CompressionFunction]]
delay: bool
watch: bool
mode: str
buffering: int
encoding: str
errors: Optional[str]
newline: Optional[str]
closefd: bool
opener: Optional[StandardOpener]
class AsyncHandlerConfig(TypedDict, total=False):
sink: Callable[[Message], Awaitable[None]]
level: Union[str, int]
format: Union[str, FormatFunction]
filter: Optional[Union[str, FilterFunction, FilterDict]]
colorize: Optional[bool]
serialize: bool
backtrace: bool
diagnose: bool
enqueue: bool
catch: bool
context: Optional[Union[str, BaseContext]]
loop: Optional[AbstractEventLoop]
HandlerConfig = Union[BasicHandlerConfig, FileHandlerConfig, AsyncHandlerConfig]
class LevelConfig(TypedDict, total=False):
name: str
no: int
color: str
icon: str
ActivationConfig = Tuple[Optional[str], bool]
class Logger:
@overload
def add(
self,
sink: Union[TextIO, Writable, Callable[[Message], None], Handler],
*,
level: Union[str, int] = ...,
format: Union[str, FormatFunction] = ...,
filter: Optional[Union[str, FilterFunction, FilterDict]] = ...,
colorize: Optional[bool] = ...,
serialize: bool = ...,
backtrace: bool = ...,
diagnose: bool = ...,
enqueue: bool = ...,
context: Optional[Union[str, BaseContext]] = ...,
catch: bool = ...
) -> int: ...
@overload
def add(
self,
sink: Callable[[Message], Awaitable[None]],
*,
level: Union[str, int] = ...,
format: Union[str, FormatFunction] = ...,
filter: Optional[Union[str, FilterFunction, FilterDict]] = ...,
colorize: Optional[bool] = ...,
serialize: bool = ...,
backtrace: bool = ...,
diagnose: bool = ...,
enqueue: bool = ...,
catch: bool = ...,
context: Optional[Union[str, BaseContext]] = ...,
loop: Optional[AbstractEventLoop] = ...
) -> int: ...
@overload
def add(
self,
sink: Union[str, PathLikeStr],
*,
level: Union[str, int] = ...,
format: Union[str, FormatFunction] = ...,
filter: Optional[Union[str, FilterFunction, FilterDict]] = ...,
colorize: Optional[bool] = ...,
serialize: bool = ...,
backtrace: bool = ...,
diagnose: bool = ...,
enqueue: bool = ...,
context: Optional[Union[str, BaseContext]] = ...,
catch: bool = ...,
rotation: Optional[
Union[
str,
int,
time,
timedelta,
RotationFunction,
list[Union[str, int, time, timedelta, RotationFunction]],
]
] = ...,
retention: Optional[Union[str, int, timedelta, RetentionFunction]] = ...,
compression: Optional[Union[str, CompressionFunction]] = ...,
delay: bool = ...,
watch: bool = ...,
mode: str = ...,
buffering: int = ...,
encoding: str = ...,
errors: Optional[str] = ...,
newline: Optional[str] = ...,
closefd: bool = ...,
opener: Optional[StandardOpener] = ...,
) -> int: ...
def remove(self, handler_id: Optional[int] = ...) -> None: ...
def complete(self) -> AwaitableCompleter: ...
@overload
def catch(
self,
exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = ...,
*,
level: Union[str, int] = ...,
reraise: bool = ...,
onerror: Optional[Callable[[BaseException], None]] = ...,
exclude: Optional[Union[Type[BaseException], Tuple[Type[BaseException], ...]]] = ...,
default: Any = ...,
message: str = ...
) -> Catcher: ...
@overload
def catch(self, function: _F) -> _F: ...
def opt(
self,
*,
exception: Optional[Union[bool, ExcInfo, BaseException]] = ...,
record: bool = ...,
lazy: bool = ...,
colors: bool = ...,
raw: bool = ...,
capture: bool = ...,
depth: int = ...,
ansi: bool = ...
) -> Logger: ...
def bind(__self, **kwargs: Any) -> Logger: ... # noqa: N805
def contextualize(__self, **kwargs: Any) -> Contextualizer: ... # noqa: N805
def patch(self, patcher: PatcherFunction) -> Logger: ...
@overload
def level(self, name: str) -> Level: ...
@overload
def level(
self, name: str, no: int = ..., color: Optional[str] = ..., icon: Optional[str] = ...
) -> Level: ...
@overload
def level(
self,
name: str,
no: Optional[int] = ...,
color: Optional[str] = ...,
icon: Optional[str] = ...,
) -> Level: ...
def disable(self, name: Optional[str]) -> None: ...
def enable(self, name: Optional[str]) -> None: ...
def configure(
self,
*,
handlers: Optional[Sequence[HandlerConfig]] = ...,
levels: Optional[Sequence[LevelConfig]] = ...,
extra: Optional[Dict[Any, Any]] = ...,
patcher: Optional[PatcherFunction] = ...,
activation: Optional[Sequence[ActivationConfig]] = ...
) -> List[int]: ...
def reinstall(self) -> None: ...
# @staticmethod cannot be used with @overload in mypy (python/mypy#7781).
# However Logger is not exposed and logger is an instance of Logger
# so for type checkers it is all the same whether it is defined here
# as a static method or an instance method.
@overload
def parse(
self,
file: Union[str, PathLikeStr, TextIO],
pattern: Union[str, Pattern[str]],
*,
cast: Union[Dict[str, Callable[[str], Any]], Callable[[Dict[str, str]], None]] = ...,
chunk: int = ...
) -> Generator[Dict[str, Any], None, None]: ...
@overload
def parse(
self,
file: BinaryIO,
pattern: Union[bytes, Pattern[bytes]],
*,
cast: Union[Dict[str, Callable[[bytes], Any]], Callable[[Dict[str, bytes]], None]] = ...,
chunk: int = ...
) -> Generator[Dict[str, Any], None, None]: ...
@overload
def trace(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def trace(__self, __message: Any) -> None: ... # noqa: N805
@overload
def debug(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def debug(__self, __message: Any) -> None: ... # noqa: N805
@overload
def info(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def info(__self, __message: Any) -> None: ... # noqa: N805
@overload
def success(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def success(__self, __message: Any) -> None: ... # noqa: N805
@overload
def warning(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def warning(__self, __message: Any) -> None: ... # noqa: N805
@overload
def error(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def error(__self, __message: Any) -> None: ... # noqa: N805
@overload
def critical(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def critical(__self, __message: Any) -> None: ... # noqa: N805
@overload
def exception(__self, __message: str, *args: Any, **kwargs: Any) -> None: ... # noqa: N805
@overload
def exception(__self, __message: Any) -> None: ... # noqa: N805
@overload
def log(
__self, __level: Union[int, str], __message: str, *args: Any, **kwargs: Any # noqa: N805
) -> None: ...
@overload
def log(__self, __level: Union[int, str], __message: Any) -> None: ... # noqa: N805
def start(self, *args: Any, **kwargs: Any) -> int: ...
def stop(self, *args: Any, **kwargs: Any) -> None: ...
logger: Logger
================================================
FILE: loguru/_asyncio_loop.py
================================================
import asyncio
import sys
def load_loop_functions():
if sys.version_info >= (3, 7):
def get_task_loop(task):
return task.get_loop()
get_running_loop = asyncio.get_running_loop
else:
def get_task_loop(task):
return task._loop
def get_running_loop():
loop = asyncio.get_event_loop()
if not loop.is_running():
raise RuntimeError("There is no running event loop")
return loop
return get_task_loop, get_running_loop
get_task_loop, get_running_loop = load_loop_functions()
================================================
FILE: loguru/_better_exceptions.py
================================================
import builtins
import inspect
import io
import keyword
import linecache
import os
import re
import sys
import sysconfig
import tokenize
import traceback
if sys.version_info >= (3, 11):
def is_exception_group(exc):
return isinstance(exc, ExceptionGroup)
else:
try:
from exceptiongroup import ExceptionGroup
except ImportError:
def is_exception_group(exc):
return False
else:
def is_exception_group(exc):
return isinstance(exc, ExceptionGroup)
class SyntaxHighlighter:
_default_style = frozenset(
{
"comment": "\x1b[30m\x1b[1m{}\x1b[0m",
"keyword": "\x1b[35m\x1b[1m{}\x1b[0m",
"builtin": "\x1b[1m{}\x1b[0m",
"string": "\x1b[36m{}\x1b[0m",
"number": "\x1b[34m\x1b[1m{}\x1b[0m",
"operator": "\x1b[35m\x1b[1m{}\x1b[0m",
"punctuation": "\x1b[1m{}\x1b[0m",
"constant": "\x1b[36m\x1b[1m{}\x1b[0m",
"identifier": "\x1b[1m{}\x1b[0m",
"other": "{}",
}.items()
)
_builtins = frozenset(dir(builtins))
_constants = frozenset({"True", "False", "None"})
_punctuation = frozenset({"(", ")", "[", "]", "{", "}", ":", ",", ";"})
if sys.version_info >= (3, 12):
_strings = frozenset(
{tokenize.STRING, tokenize.FSTRING_START, tokenize.FSTRING_MIDDLE, tokenize.FSTRING_END}
)
_fstring_middle = tokenize.FSTRING_MIDDLE
else:
_strings = frozenset({tokenize.STRING})
_fstring_middle = None
def __init__(self, style=None):
self._style = style or dict(self._default_style)
def highlight(self, source):
style = self._style
row, column = 0, 0
output = ""
for token in self.tokenize(source):
type_, string, (start_row, start_column), (_, end_column), line = token
if type_ == self._fstring_middle:
# When an f-string contains "{{" or "}}", they appear as "{" or "}" in the "string"
# attribute of the token. However, they do not count in the column position.
end_column += string.count("{") + string.count("}")
if type_ == tokenize.NAME:
if string in self._constants:
color = style["constant"]
elif keyword.iskeyword(string):
color = style["keyword"]
elif string in self._builtins:
color = style["builtin"]
else:
color = style["identifier"]
elif type_ == tokenize.OP:
if string in self._punctuation:
color = style["punctuation"]
else:
color = style["operator"]
elif type_ == tokenize.NUMBER:
color = style["number"]
elif type_ in self._strings:
color = style["string"]
elif type_ == tokenize.COMMENT:
color = style["comment"]
else:
color = style["other"]
if start_row != row:
source = source[column:]
row, column = start_row, 0
if type_ != tokenize.ENCODING:
output += line[column:start_column]
output += color.format(line[start_column:end_column])
column = end_column
output += source[column:]
return output
@staticmethod
def tokenize(source):
# Worth reading: https://www.asmeurer.com/brown-water-python/
source = source.encode("utf-8")
source = io.BytesIO(source)
try:
yield from tokenize.tokenize(source.readline)
except tokenize.TokenError:
return
class ExceptionFormatter:
_default_theme = frozenset(
{
"introduction": "\x1b[33m\x1b[1m{}\x1b[0m",
"cause": "\x1b[1m{}\x1b[0m",
"context": "\x1b[1m{}\x1b[0m",
"dirname": "\x1b[32m{}\x1b[0m",
"basename": "\x1b[32m\x1b[1m{}\x1b[0m",
"line": "\x1b[33m{}\x1b[0m",
"function": "\x1b[35m{}\x1b[0m",
"exception_type": "\x1b[31m\x1b[1m{}\x1b[0m",
"exception_value": "\x1b[1m{}\x1b[0m",
"arrows": "\x1b[36m{}\x1b[0m",
"value": "\x1b[36m\x1b[1m{}\x1b[0m",
}.items()
)
def __init__(
self,
colorize=False,
backtrace=False,
diagnose=True,
theme=None,
style=None,
max_length=128,
encoding="ascii",
hidden_frames_filename=None,
prefix="",
):
self._colorize = colorize
self._diagnose = diagnose
self._theme = theme or dict(self._default_theme)
self._backtrace = backtrace
self._syntax_highlighter = SyntaxHighlighter(style)
self._max_length = max_length
self._enco
gitextract_f3vpu0bp/ ├── .codecov.yml ├── .github/ │ ├── SECURITY.md │ ├── dependabot.yml │ └── workflows/ │ ├── codeql-analysis.yml │ ├── docs.yml │ ├── lint.yml │ ├── packaging.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── .vscode/ │ └── settings.json ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── LICENSE ├── README.md ├── docs/ │ ├── Makefile │ ├── _static/ │ │ ├── css/ │ │ │ └── loguru.css │ │ └── js/ │ │ └── copybutton.js │ ├── _templates/ │ │ ├── breadcrumbs.html │ │ └── layout.html │ ├── api/ │ │ ├── logger.rst │ │ ├── type_hints.rst │ │ └── type_hints_source.rst │ ├── api.rst │ ├── conf.py │ ├── index.rst │ ├── overview.rst │ ├── project/ │ │ ├── changelog.rst │ │ ├── contributing.rst │ │ └── license.rst │ ├── project.rst │ ├── resources/ │ │ ├── migration.rst │ │ ├── recipes.rst │ │ └── troubleshooting.rst │ └── resources.rst ├── loguru/ │ ├── __init__.py │ ├── __init__.pyi │ ├── _asyncio_loop.py │ ├── _better_exceptions.py │ ├── _colorama.py │ ├── _colorizer.py │ ├── _contextvars.py │ ├── _ctime_functions.py │ ├── _datetime.py │ ├── _defaults.py │ ├── _error_interceptor.py │ ├── _file_sink.py │ ├── _filters.py │ ├── _get_frame.py │ ├── _handler.py │ ├── _locks_machinery.py │ ├── _logger.py │ ├── _recattrs.py │ ├── _simple_sinks.py │ ├── _string_parsers.py │ └── py.typed ├── pyproject.toml ├── tests/ │ ├── __init__.py │ ├── conftest.py │ ├── exceptions/ │ │ ├── output/ │ │ │ ├── backtrace/ │ │ │ │ ├── chained_expression_direct.txt │ │ │ │ ├── chained_expression_indirect.txt │ │ │ │ ├── chaining_first.txt │ │ │ │ ├── chaining_second.txt │ │ │ │ ├── chaining_third.txt │ │ │ │ ├── enqueue.txt │ │ │ │ ├── enqueue_with_others_handlers.txt │ │ │ │ ├── frame_values_backward.txt │ │ │ │ ├── frame_values_forward.txt │ │ │ │ ├── function.txt │ │ │ │ ├── head_recursion.txt │ │ │ │ ├── missing_attributes_traceback_objects.txt │ │ │ │ ├── missing_lineno_frame_objects.txt │ │ │ │ ├── nested.txt │ │ │ │ ├── nested_chained_catch_up.txt │ │ │ │ ├── nested_decorator_catch_up.txt │ │ │ │ ├── nested_explicit_catch_up.txt │ │ │ │ ├── nested_wrapping.txt │ │ │ │ ├── no_tb.txt │ │ │ │ ├── not_enough_arguments.txt │ │ │ │ ├── raising_recursion.txt │ │ │ │ ├── suppressed_expression_direct.txt │ │ │ │ ├── suppressed_expression_indirect.txt │ │ │ │ ├── tail_recursion.txt │ │ │ │ └── too_many_arguments.txt │ │ │ ├── diagnose/ │ │ │ │ ├── assertion_error.txt │ │ │ │ ├── assertion_error_custom.txt │ │ │ │ ├── assertion_error_in_string.txt │ │ │ │ ├── attributes.txt │ │ │ │ ├── chained_both.txt │ │ │ │ ├── encoding.txt │ │ │ │ ├── global_variable.txt │ │ │ │ ├── indentation_error.txt │ │ │ │ ├── keyword_argument.txt │ │ │ │ ├── multilines_repr.txt │ │ │ │ ├── no_error_message.txt │ │ │ │ ├── parenthesis.txt │ │ │ │ ├── source_multilines.txt │ │ │ │ ├── source_strings.txt │ │ │ │ ├── syntax_error.txt │ │ │ │ ├── syntax_highlighting.txt │ │ │ │ ├── truncating.txt │ │ │ │ └── unprintable_object.txt │ │ │ ├── modern/ │ │ │ │ ├── decorate_async_generator.txt │ │ │ │ ├── exception_formatting_async_generator.txt │ │ │ │ ├── exception_group_catch.txt │ │ │ │ ├── f_string.txt │ │ │ │ ├── grouped_as_cause_and_context.txt │ │ │ │ ├── grouped_max_depth.txt │ │ │ │ ├── grouped_max_length.txt │ │ │ │ ├── grouped_nested.txt │ │ │ │ ├── grouped_simple.txt │ │ │ │ ├── grouped_with_cause_and_context.txt │ │ │ │ ├── match_statement.txt │ │ │ │ ├── notes.txt │ │ │ │ ├── positional_only_argument.txt │ │ │ │ ├── type_hints.txt │ │ │ │ └── walrus_operator.txt │ │ │ ├── others/ │ │ │ │ ├── assertionerror_without_traceback.txt │ │ │ │ ├── broken_but_decorated_repr.txt │ │ │ │ ├── catch_as_context_manager.txt │ │ │ │ ├── catch_as_decorator_with_parentheses.txt │ │ │ │ ├── catch_as_decorator_without_parentheses.txt │ │ │ │ ├── catch_as_function.txt │ │ │ │ ├── catch_message.txt │ │ │ │ ├── exception_formatting_coroutine.txt │ │ │ │ ├── exception_formatting_function.txt │ │ │ │ ├── exception_formatting_generator.txt │ │ │ │ ├── exception_in_property.txt │ │ │ │ ├── handler_formatting_with_context_manager.txt │ │ │ │ ├── handler_formatting_with_decorator.txt │ │ │ │ ├── level_name.txt │ │ │ │ ├── level_number.txt │ │ │ │ ├── message_formatting_with_context_manager.txt │ │ │ │ ├── message_formatting_with_decorator.txt │ │ │ │ ├── nested_with_reraise.txt │ │ │ │ ├── one_liner_recursion.txt │ │ │ │ ├── recursion_error.txt │ │ │ │ ├── repeated_lines.txt │ │ │ │ ├── syntaxerror_without_traceback.txt │ │ │ │ ├── sys_tracebacklimit.txt │ │ │ │ ├── sys_tracebacklimit_negative.txt │ │ │ │ ├── sys_tracebacklimit_none.txt │ │ │ │ ├── sys_tracebacklimit_unset.txt │ │ │ │ └── zerodivisionerror_without_traceback.txt │ │ │ └── ownership/ │ │ │ ├── assertion_from_lib.txt │ │ │ ├── assertion_from_local.txt │ │ │ ├── callback.txt │ │ │ ├── catch_decorator.txt │ │ │ ├── catch_decorator_from_lib.txt │ │ │ ├── decorated_callback.txt │ │ │ ├── direct.txt │ │ │ ├── indirect.txt │ │ │ ├── string_lib.txt │ │ │ ├── string_source.txt │ │ │ └── syntaxerror.txt │ │ └── source/ │ │ ├── backtrace/ │ │ │ ├── chained_expression_direct.py │ │ │ ├── chained_expression_indirect.py │ │ │ ├── chaining_first.py │ │ │ ├── chaining_second.py │ │ │ ├── chaining_third.py │ │ │ ├── enqueue.py │ │ │ ├── enqueue_with_others_handlers.py │ │ │ ├── frame_values_backward.py │ │ │ ├── frame_values_forward.py │ │ │ ├── function.py │ │ │ ├── head_recursion.py │ │ │ ├── missing_attributes_traceback_objects.py │ │ │ ├── missing_lineno_frame_objects.py │ │ │ ├── nested.py │ │ │ ├── nested_chained_catch_up.py │ │ │ ├── nested_decorator_catch_up.py │ │ │ ├── nested_explicit_catch_up.py │ │ │ ├── nested_wrapping.py │ │ │ ├── no_tb.py │ │ │ ├── not_enough_arguments.py │ │ │ ├── raising_recursion.py │ │ │ ├── suppressed_expression_direct.py │ │ │ ├── suppressed_expression_indirect.py │ │ │ ├── tail_recursion.py │ │ │ └── too_many_arguments.py │ │ ├── diagnose/ │ │ │ ├── assertion_error.py │ │ │ ├── assertion_error_custom.py │ │ │ ├── assertion_error_in_string.py │ │ │ ├── attributes.py │ │ │ ├── chained_both.py │ │ │ ├── encoding.py │ │ │ ├── global_variable.py │ │ │ ├── indentation_error.py │ │ │ ├── keyword_argument.py │ │ │ ├── multilines_repr.py │ │ │ ├── no_error_message.py │ │ │ ├── parenthesis.py │ │ │ ├── source_multilines.py │ │ │ ├── source_strings.py │ │ │ ├── syntax_error.py │ │ │ ├── syntax_highlighting.py │ │ │ ├── truncating.py │ │ │ └── unprintable_object.py │ │ ├── modern/ │ │ │ ├── decorate_async_generator.py │ │ │ ├── exception_formatting_async_generator.py │ │ │ ├── exception_group_catch.py │ │ │ ├── f_string.py │ │ │ ├── grouped_as_cause_and_context.py │ │ │ ├── grouped_max_depth.py │ │ │ ├── grouped_max_length.py │ │ │ ├── grouped_nested.py │ │ │ ├── grouped_simple.py │ │ │ ├── grouped_with_cause_and_context.py │ │ │ ├── match_statement.py │ │ │ ├── notes.py │ │ │ ├── positional_only_argument.py │ │ │ ├── type_hints.py │ │ │ └── walrus_operator.py │ │ ├── others/ │ │ │ ├── assertionerror_without_traceback.py │ │ │ ├── broken_but_decorated_repr.py │ │ │ ├── catch_as_context_manager.py │ │ │ ├── catch_as_decorator_with_parentheses.py │ │ │ ├── catch_as_decorator_without_parentheses.py │ │ │ ├── catch_as_function.py │ │ │ ├── catch_message.py │ │ │ ├── exception_formatting_coroutine.py │ │ │ ├── exception_formatting_function.py │ │ │ ├── exception_formatting_generator.py │ │ │ ├── exception_in_property.py │ │ │ ├── handler_formatting_with_context_manager.py │ │ │ ├── handler_formatting_with_decorator.py │ │ │ ├── level_name.py │ │ │ ├── level_number.py │ │ │ ├── message_formatting_with_context_manager.py │ │ │ ├── message_formatting_with_decorator.py │ │ │ ├── nested_with_reraise.py │ │ │ ├── one_liner_recursion.py │ │ │ ├── recursion_error.py │ │ │ ├── repeated_lines.py │ │ │ ├── syntaxerror_without_traceback.py │ │ │ ├── sys_tracebacklimit.py │ │ │ ├── sys_tracebacklimit_negative.py │ │ │ ├── sys_tracebacklimit_none.py │ │ │ ├── sys_tracebacklimit_unset.py │ │ │ └── zerodivisionerror_without_traceback.py │ │ └── ownership/ │ │ ├── _init.py │ │ ├── assertion_from_lib.py │ │ ├── assertion_from_local.py │ │ ├── callback.py │ │ ├── catch_decorator.py │ │ ├── catch_decorator_from_lib.py │ │ ├── decorated_callback.py │ │ ├── direct.py │ │ ├── indirect.py │ │ ├── string_lib.py │ │ ├── string_source.py │ │ ├── syntaxerror.py │ │ └── usersite/ │ │ └── somelib/ │ │ └── __init__.py │ ├── test_activation.py │ ├── test_add_option_backtrace.py │ ├── test_add_option_catch.py │ ├── test_add_option_colorize.py │ ├── test_add_option_context.py │ ├── test_add_option_diagnose.py │ ├── test_add_option_enqueue.py │ ├── test_add_option_filter.py │ ├── test_add_option_format.py │ ├── test_add_option_kwargs.py │ ├── test_add_option_level.py │ ├── test_add_option_serialize.py │ ├── test_add_sinks.py │ ├── test_ansimarkup_basic.py │ ├── test_ansimarkup_extended.py │ ├── test_bind.py │ ├── test_colorama.py │ ├── test_configure.py │ ├── test_contextualize.py │ ├── test_coroutine_sink.py │ ├── test_datetime.py │ ├── test_deepcopy.py │ ├── test_defaults.py │ ├── test_exceptions_catch.py │ ├── test_exceptions_formatting.py │ ├── test_filesink_compression.py │ ├── test_filesink_delay.py │ ├── test_filesink_permissions.py │ ├── test_filesink_retention.py │ ├── test_filesink_rotation.py │ ├── test_filesink_watch.py │ ├── test_formatting.py │ ├── test_get_frame.py │ ├── test_interception.py │ ├── test_levels.py │ ├── test_locks.py │ ├── test_multiprocessing.py │ ├── test_opt.py │ ├── test_parse.py │ ├── test_patch.py │ ├── test_pickling.py │ ├── test_propagation.py │ ├── test_recattr.py │ ├── test_reinstall.py │ ├── test_remove.py │ ├── test_repr.py │ ├── test_standard_handler.py │ ├── test_threading.py │ ├── test_type_hinting.py │ └── typesafety/ │ └── test_logger.yml └── tox.ini
SYMBOL INDEX (1342 symbols across 160 files)
FILE: docs/conf.py
function setup (line 180) | def setup(app):
FILE: loguru/__init__.pyi
class _GeneratorContextManager (line 52) | class _GeneratorContextManager(ContextManager[_T], Generic[_T]):
method __call__ (line 53) | def __call__(self, func: _F) -> _F: ...
method __exit__ (line 54) | def __exit__(
class Level (line 65) | class Level(NamedTuple):
class _RecordAttribute (line 71) | class _RecordAttribute:
method __format__ (line 72) | def __format__(self, spec: str) -> str: ...
class RecordFile (line 74) | class RecordFile(_RecordAttribute):
class RecordLevel (line 78) | class RecordLevel(_RecordAttribute):
class RecordThread (line 83) | class RecordThread(_RecordAttribute):
class RecordProcess (line 87) | class RecordProcess(_RecordAttribute):
class RecordException (line 91) | class RecordException(NamedTuple):
class Record (line 96) | class Record(TypedDict):
class Message (line 111) | class Message(str):
class Writable (line 114) | class Writable(Protocol):
method write (line 115) | def write(self, message: Message) -> Any: ...
class BasicHandlerConfig (line 127) | class BasicHandlerConfig(TypedDict, total=False):
class FileHandlerConfig (line 139) | class FileHandlerConfig(TypedDict, total=False):
class AsyncHandlerConfig (line 172) | class AsyncHandlerConfig(TypedDict, total=False):
class LevelConfig (line 188) | class LevelConfig(TypedDict, total=False):
class Logger (line 196) | class Logger:
method add (line 198) | def add(
method add (line 214) | def add(
method add (line 231) | def add(
method remove (line 267) | def remove(self, handler_id: Optional[int] = ...) -> None: ...
method complete (line 268) | def complete(self) -> AwaitableCompleter: ...
method catch (line 270) | def catch(
method catch (line 282) | def catch(self, function: _F) -> _F: ...
method opt (line 283) | def opt(
method bind (line 295) | def bind(__self, **kwargs: Any) -> Logger: ... # noqa: N805
method contextualize (line 296) | def contextualize(__self, **kwargs: Any) -> Contextualizer: ... # noq...
method patch (line 297) | def patch(self, patcher: PatcherFunction) -> Logger: ...
method level (line 299) | def level(self, name: str) -> Level: ...
method level (line 301) | def level(
method level (line 305) | def level(
method disable (line 312) | def disable(self, name: Optional[str]) -> None: ...
method enable (line 313) | def enable(self, name: Optional[str]) -> None: ...
method configure (line 314) | def configure(
method reinstall (line 323) | def reinstall(self) -> None: ...
method parse (line 329) | def parse(
method parse (line 338) | def parse(
method trace (line 347) | def trace(__self, __message: str, *args: Any, **kwargs: Any) -> None: ...
method trace (line 349) | def trace(__self, __message: Any) -> None: ... # noqa: N805
method debug (line 351) | def debug(__self, __message: str, *args: Any, **kwargs: Any) -> None: ...
method debug (line 353) | def debug(__self, __message: Any) -> None: ... # noqa: N805
method info (line 355) | def info(__self, __message: str, *args: Any, **kwargs: Any) -> None: ....
method info (line 357) | def info(__self, __message: Any) -> None: ... # noqa: N805
method success (line 359) | def success(__self, __message: str, *args: Any, **kwargs: Any) -> None...
method success (line 361) | def success(__self, __message: Any) -> None: ... # noqa: N805
method warning (line 363) | def warning(__self, __message: str, *args: Any, **kwargs: Any) -> None...
method warning (line 365) | def warning(__self, __message: Any) -> None: ... # noqa: N805
method error (line 367) | def error(__self, __message: str, *args: Any, **kwargs: Any) -> None: ...
method error (line 369) | def error(__self, __message: Any) -> None: ... # noqa: N805
method critical (line 371) | def critical(__self, __message: str, *args: Any, **kwargs: Any) -> Non...
method critical (line 373) | def critical(__self, __message: Any) -> None: ... # noqa: N805
method exception (line 375) | def exception(__self, __message: str, *args: Any, **kwargs: Any) -> No...
method exception (line 377) | def exception(__self, __message: Any) -> None: ... # noqa: N805
method log (line 379) | def log(
method log (line 383) | def log(__self, __level: Union[int, str], __message: Any) -> None: ......
method start (line 384) | def start(self, *args: Any, **kwargs: Any) -> int: ...
method stop (line 385) | def stop(self, *args: Any, **kwargs: Any) -> None: ...
FILE: loguru/_asyncio_loop.py
function load_loop_functions (line 5) | def load_loop_functions():
FILE: loguru/_better_exceptions.py
function is_exception_group (line 15) | def is_exception_group(exc):
function is_exception_group (line 23) | def is_exception_group(exc):
function is_exception_group (line 28) | def is_exception_group(exc):
class SyntaxHighlighter (line 32) | class SyntaxHighlighter:
method __init__ (line 61) | def __init__(self, style=None):
method highlight (line 64) | def highlight(self, source):
method tokenize (line 115) | def tokenize(source):
class ExceptionFormatter (line 126) | class ExceptionFormatter:
method __init__ (line 143) | def __init__(
method _get_lib_dirs (line 170) | def _get_lib_dirs():
method _indent (line 177) | def _indent(text, count, *, prefix="| "):
method _get_char (line 185) | def _get_char(self, char, default):
method _is_file_mine (line 193) | def _is_file_mine(self, file):
method _should_include_frame (line 199) | def _should_include_frame(self, frame):
method _extract_frames (line 202) | def _extract_frames(self, tb, is_first, *, limit=None, from_decorator=...
method _get_relevant_values (line 267) | def _get_relevant_values(self, source, frame):
method _format_relevant_values (line 316) | def _format_relevant_values(self, relevant_values, colorize):
method _format_value (line 342) | def _format_value(self, v):
method _format_locations (line 353) | def _format_locations(self, frames_lines, *, has_introduction):
method _format_exception (line 393) | def _format_exception(
method _format_list (line 540) | def _format_list(self, frames):
method format_exception (line 576) | def format_exception(self, type_, value, tb, *, from_decorator=False):
FILE: loguru/_colorama.py
function should_colorize (line 6) | def should_colorize(stream):
function should_wrap (line 57) | def should_wrap(stream):
function wrap (line 80) | def wrap(stream):
FILE: loguru/_colorizer.py
function try_formatting (line 7) | def try_formatting(*exceptions):
class Style (line 26) | class Style:
class Fore (line 39) | class Fore:
class Back (line 60) | class Back:
function ansi_escape (line 81) | def ansi_escape(codes):
class TokenType (line 85) | class TokenType:
class AnsiParser (line 92) | class AnsiParser:
method __init__ (line 192) | def __init__(self):
method strip (line 198) | def strip(tokens):
method colorize (line 206) | def colorize(tokens, ansi_level):
method wrap (line 222) | def wrap(tokens, *, ansi_level, color_tokens):
method feed (line 237) | def feed(self, text, *, raw=False):
method done (line 295) | def done(self, *, strict=True):
method current_color_tokens (line 301) | def current_color_tokens(self):
method _get_ansicode (line 304) | def _get_ansicode(self, tag):
class ColoringMessage (line 347) | class ColoringMessage(str):
method __format__ (line 350) | def __format__(self, spec):
class ColoredMessage (line 354) | class ColoredMessage:
method __init__ (line 355) | def __init__(self, tokens):
method colorize (line 359) | def colorize(self, ansi_level):
class ColoredFormat (line 363) | class ColoredFormat:
method __init__ (line 364) | def __init__(self, tokens, messages_color_tokens):
method strip (line 368) | def strip(self):
method colorize (line 371) | def colorize(self, ansi_level):
method make_coloring_message (line 374) | def make_coloring_message(self, message, *, ansi_level, colored_message):
class Colorizer (line 390) | class Colorizer:
method prepare_format (line 392) | def prepare_format(string):
method prepare_message (line 397) | def prepare_message(string, args=(), kwargs={}): # noqa: B006
method prepare_simple_message (line 402) | def prepare_simple_message(string):
method ansify (line 409) | def ansify(text):
method _parse_with_formatting (line 416) | def _parse_with_formatting(
method _parse_without_formatting (line 477) | def _parse_without_formatting(string, *, recursion_depth=2, recursive=...
FILE: loguru/_contextvars.py
function load_contextvar_class (line 4) | def load_contextvar_class():
FILE: loguru/_ctime_functions.py
function load_ctime_functions (line 4) | def load_ctime_functions():
FILE: loguru/_datetime.py
function _builtin_datetime_formatter (line 13) | def _builtin_datetime_formatter(is_utc, format_string, dt):
function _loguru_datetime_formatter (line 19) | def _loguru_datetime_formatter(is_utc, format_string, formatters, dt):
function _default_datetime_formatter (line 27) | def _default_datetime_formatter(dt):
function _format_timezone (line 40) | def _format_timezone(dt, *, sep):
function _compile_format (line 55) | def _compile_format(spec):
class datetime (line 139) | class datetime(datetime_): # noqa: N801
method __format__ (line 140) | def __format__(self, fmt):
function _fallback_tzinfo (line 144) | def _fallback_tzinfo(timestamp):
function _get_tzinfo (line 152) | def _get_tzinfo(timestamp):
function aware_now (line 175) | def aware_now():
FILE: loguru/_defaults.py
function env (line 4) | def env(key, type_, default=None):
FILE: loguru/_error_interceptor.py
class ErrorInterceptor (line 5) | class ErrorInterceptor:
method __init__ (line 6) | def __init__(self, should_catch, handler_id):
method should_catch (line 10) | def should_catch(self):
method print (line 13) | def print(self, record=None, *, exception=None):
FILE: loguru/_file_sink.py
function generate_rename_path (line 16) | def generate_rename_path(root, ext, creation_time):
class FileDateFormatter (line 30) | class FileDateFormatter:
method __init__ (line 31) | def __init__(self, datetime=None):
method __format__ (line 34) | def __format__(self, spec):
class Compression (line 40) | class Compression:
method add_compress (line 42) | def add_compress(path_in, path_out, opener, **kwargs):
method write_compress (line 47) | def write_compress(path_in, path_out, opener, **kwargs):
method copy_compress (line 52) | def copy_compress(path_in, path_out, opener, **kwargs):
method compression (line 58) | def compression(path_in, ext, compress_function):
class Retention (line 70) | class Retention:
method retention_count (line 72) | def retention_count(logs, number):
method retention_age (line 80) | def retention_age(logs, seconds):
class Rotation (line 87) | class Rotation:
method forward_day (line 89) | def forward_day(t):
method forward_weekday (line 93) | def forward_weekday(t, weekday):
method forward_interval (line 100) | def forward_interval(t, interval):
method rotation_size (line 104) | def rotation_size(message, file, size_limit):
class RotationTime (line 108) | class RotationTime:
method __init__ (line 109) | def __init__(self, step_forward, time_init=None):
method __call__ (line 114) | def __call__(self, message, file):
class RotationGroup (line 156) | class RotationGroup:
method __init__ (line 157) | def __init__(self, rotations) -> None:
method __call__ (line 160) | def __call__(self, message, file) -> bool:
class FileSink (line 164) | class FileSink:
method __init__ (line 165) | def __init__(
method write (line 201) | def write(self, message):
method stop (line 215) | def stop(self):
method tasks_to_complete (line 221) | def tasks_to_complete(self):
method _create_path (line 224) | def _create_path(self):
method _create_dirs (line 228) | def _create_dirs(self, path):
method _create_file (line 232) | def _create_file(self, path):
method _close_file (line 242) | def _close_file(self):
method _reopen_if_needed (line 251) | def _reopen_if_needed(self):
method _terminate_file (line 269) | def _terminate_file(self, *, is_rotating=False):
method _make_glob_patterns (line 304) | def _make_glob_patterns(path):
method _make_rotation_function (line 317) | def _make_rotation_function(rotation):
method _make_retention_function (line 358) | def _make_retention_function(retention):
method _make_compression_function (line 377) | def _make_compression_function(compression):
FILE: loguru/_filters.py
function filter_none (line 1) | def filter_none(record):
function filter_by_name (line 5) | def filter_by_name(record, parent, length):
function filter_by_level (line 12) | def filter_by_level(record, level_per_module):
FILE: loguru/_get_frame.py
function get_frame_fallback (line 5) | def get_frame_fallback(n):
function load_get_frame_function (line 15) | def load_get_frame_function():
FILE: loguru/_handler.py
function prepare_colored_format (line 13) | def prepare_colored_format(format_, ansi_level):
function prepare_stripped_format (line 18) | def prepare_stripped_format(format_):
function memoize (line 23) | def memoize(function):
class Message (line 27) | class Message(str):
class Handler (line 31) | class Handler:
method __init__ (line 32) | def __init__(
method __repr__ (line 107) | def __repr__(self):
method _protected_lock (line 111) | def _protected_lock(self):
method emit (line 127) | def emit(self, record, level_id, from_decorator, is_raw, colored_messa...
method stop (line 212) | def stop(self):
method complete_queue (line 225) | def complete_queue(self):
method tasks_to_complete (line 234) | def tasks_to_complete(self):
method update_format (line 241) | def update_format(self, level_id):
method levelno (line 248) | def levelno(self):
method _serialize_record (line 252) | def _serialize_record(text, record):
method _queued_writer (line 290) | def _queued_writer(self):
method __getstate__ (line 319) | def __getstate__(self):
method __setstate__ (line 330) | def __setstate__(self, state):
FILE: loguru/_locks_machinery.py
function create_logger_lock (line 7) | def create_logger_lock():
function create_handler_lock (line 10) | def create_handler_lock():
function acquire_locks (line 22) | def acquire_locks():
function release_locks (line 29) | def release_locks():
function create_logger_lock (line 42) | def create_logger_lock():
function create_handler_lock (line 47) | def create_handler_lock():
FILE: loguru/_logger.py
function isasyncgenfunction (line 135) | def isasyncgenfunction(func):
class Core (line 146) | class Core:
method __init__ (line 147) | def __init__(self):
method __getstate__ (line 219) | def __getstate__(self):
method __setstate__ (line 225) | def __setstate__(self, state):
class Logger (line 231) | class Logger:
method __init__ (line 254) | def __init__(self, core, exception, depth, record, lazy, colors, raw, ...
method __repr__ (line 258) | def __repr__(self):
method add (line 261) | def add(
method remove (line 1056) | def remove(self, handler_id=None):
method complete (line 1104) | def complete(self):
method catch (line 1169) | def catch(
method opt (line 1371) | def opt(
method bind (line 1471) | def bind(__self, **kwargs): # noqa: N805
method contextualize (line 1508) | def contextualize(__self, **kwargs): # noqa: N805
method patch (line 1552) | def patch(self, patcher):
method level (line 1601) | def level(self, name, no=None, color=None, icon=None):
method disable (line 1704) | def disable(self, name):
method enable (line 1728) | def enable(self, name):
method configure (line 1752) | def configure(self, *, handlers=None, levels=None, extra=None, patcher...
method reinstall (line 1842) | def reinstall(self):
method _change_activation (line 1873) | def _change_activation(self, name, status):
method parse (line 1914) | def parse(file, pattern, *, cast={}, chunk=2**16): # noqa: B006
method _find_iter (line 2011) | def _find_iter(fileobj, regex, chunk):
method _log (line 2028) | def _log(self, level, from_decorator, options, message, args, kwargs):
method trace (line 2166) | def trace(__self, __message, *args, **kwargs): # noqa: N805
method debug (line 2170) | def debug(__self, __message, *args, **kwargs): # noqa: N805
method info (line 2174) | def info(__self, __message, *args, **kwargs): # noqa: N805
method success (line 2178) | def success(__self, __message, *args, **kwargs): # noqa: N805
method warning (line 2182) | def warning(__self, __message, *args, **kwargs): # noqa: N805
method error (line 2186) | def error(__self, __message, *args, **kwargs): # noqa: N805
method critical (line 2190) | def critical(__self, __message, *args, **kwargs): # noqa: N805
method exception (line 2194) | def exception(__self, __message, *args, **kwargs): # noqa: N805
method log (line 2204) | def log(__self, __level, __message, *args, **kwargs): # noqa: N805
method start (line 2212) | def start(self, *args, **kwargs):
method stop (line 2230) | def stop(self, *args, **kwargs):
FILE: loguru/_recattrs.py
class RecordLevel (line 5) | class RecordLevel:
method __init__ (line 20) | def __init__(self, name, no, icon):
method __repr__ (line 36) | def __repr__(self):
method __format__ (line 46) | def __format__(self, spec):
class RecordFile (line 62) | class RecordFile:
method __init__ (line 75) | def __init__(self, name, path):
method __repr__ (line 88) | def __repr__(self):
method __format__ (line 98) | def __format__(self, spec):
class RecordThread (line 114) | class RecordThread:
method __init__ (line 127) | def __init__(self, id_, name):
method __repr__ (line 140) | def __repr__(self):
method __format__ (line 150) | def __format__(self, spec):
class RecordProcess (line 166) | class RecordProcess:
method __init__ (line 179) | def __init__(self, id_, name):
method __repr__ (line 192) | def __repr__(self):
method __format__ (line 202) | def __format__(self, spec):
class RecordException (line 218) | class RecordException(
method __repr__ (line 233) | def __repr__(self):
method __reduce__ (line 243) | def __reduce__(self):
method _from_pickled_value (line 269) | def _from_pickled_value(cls, type_, pickled_value, traceback_):
FILE: loguru/_simple_sinks.py
class StreamSink (line 8) | class StreamSink:
method __init__ (line 17) | def __init__(self, stream):
method write (line 23) | def write(self, message):
method stop (line 35) | def stop(self):
method tasks_to_complete (line 40) | def tasks_to_complete(self):
class StandardSink (line 53) | class StandardSink:
method __init__ (line 62) | def __init__(self, handler):
method write (line 65) | def write(self, message):
method stop (line 101) | def stop(self):
method tasks_to_complete (line 105) | def tasks_to_complete(self):
class AsyncSink (line 116) | class AsyncSink:
method __init__ (line 129) | def __init__(self, function, loop, error_interceptor):
method write (line 135) | def write(self, message):
method stop (line 161) | def stop(self):
method tasks_to_complete (line 166) | def tasks_to_complete(self):
method _complete_task (line 181) | async def _complete_task(self, task):
method __getstate__ (line 197) | def __getstate__(self):
method __setstate__ (line 202) | def __setstate__(self, state):
class CallableSink (line 207) | class CallableSink:
method __init__ (line 216) | def __init__(self, function):
method write (line 219) | def write(self, message):
method stop (line 229) | def stop(self):
method tasks_to_complete (line 233) | def tasks_to_complete(self):
FILE: loguru/_string_parsers.py
class Frequencies (line 6) | class Frequencies:
method hourly (line 14) | def hourly(t: datetime.datetime) -> datetime.datetime:
method daily (line 31) | def daily(t: datetime.datetime) -> datetime.datetime:
method weekly (line 48) | def weekly(t: datetime.datetime) -> datetime.datetime:
method monthly (line 65) | def monthly(t: datetime.datetime) -> datetime.datetime:
method yearly (line 85) | def yearly(t: datetime.datetime) -> datetime.datetime:
function parse_size (line 102) | def parse_size(size: str) -> Optional[float]:
function parse_duration (line 143) | def parse_duration(duration: str) -> Optional[datetime.timedelta]:
function parse_frequency (line 200) | def parse_frequency(frequency: str):
function parse_day (line 226) | def parse_day(day: str) -> Optional[int]:
function parse_time (line 268) | def parse_time(time: str) -> datetime.time:
function parse_daytime (line 316) | def parse_daytime(daytime: str) -> Optional[Tuple[int, datetime.time]]:
FILE: tests/conftest.py
function run (line 25) | def run(coro):
function run (line 36) | def run(coro):
function tmp_path (line 48) | def tmp_path(tmp_path):
function _fix_positional_only_args (line 61) | def _fix_positional_only_args(item: YamlTestItem):
function _fix_builtins_uppercase (line 68) | def _fix_builtins_uppercase(item: YamlTestItem):
function _add_mypy_config (line 78) | def _add_mypy_config(item: YamlTestItem):
function pytest_collection_modifyitems (line 87) | def pytest_collection_modifyitems(config, items):
function new_event_loop_context (line 103) | def new_event_loop_context():
function set_event_loop_context (line 112) | def set_event_loop_context(loop):
function parse (line 120) | def parse(text, *, strip=False, strict=True):
function check_dir (line 130) | def check_dir(dir, *, files=None, size=None):
class StubStream (line 146) | class StubStream(io.StringIO):
method fileno (line 147) | def fileno(self):
class StreamIsattyTrue (line 151) | class StreamIsattyTrue(StubStream):
method isatty (line 152) | def isatty(self):
class StreamIsattyFalse (line 156) | class StreamIsattyFalse(StubStream):
method isatty (line 157) | def isatty(self):
class StreamIsattyException (line 161) | class StreamIsattyException(StubStream):
method isatty (line 162) | def isatty(self):
class StreamFilenoException (line 166) | class StreamFilenoException(StreamIsattyTrue):
method fileno (line 167) | def fileno(self):
function default_threading_excepthook (line 172) | def default_threading_excepthook():
function check_env_variables (line 193) | def check_env_variables():
function reset_logger (line 205) | def reset_logger():
function writer (line 219) | def writer():
function sink_with_logger (line 231) | def sink_with_logger():
function freeze_time (line 245) | def freeze_time(monkeypatch):
function make_logging_logger (line 346) | def make_logging_logger(name, handler, fmt="%(message)s", level="DEBUG"):
function _simulate_f_globals_name_absent (line 363) | def _simulate_f_globals_name_absent(monkeypatch):
function _simulate_no_frame_available (line 377) | def _simulate_no_frame_available(monkeypatch):
function incomplete_frame_context (line 389) | def incomplete_frame_context(request, monkeypatch):
function missing_frame_lineno_value (line 395) | def missing_frame_lineno_value(monkeypatch):
function reset_multiprocessing_start_method (line 419) | def reset_multiprocessing_start_method():
FILE: tests/exceptions/source/backtrace/chained_expression_direct.py
function a_decorated (line 10) | def a_decorated():
function a_not_decorated (line 17) | def a_not_decorated():
function b_decorator (line 24) | def b_decorator():
function b_context_manager (line 28) | def b_context_manager():
function b_explicit (line 33) | def b_explicit():
FILE: tests/exceptions/source/backtrace/chained_expression_indirect.py
function a (line 9) | def a():
function b (line 17) | def b():
FILE: tests/exceptions/source/backtrace/chaining_first.py
function a_decorated (line 10) | def a_decorated():
function a_not_decorated (line 14) | def a_not_decorated():
function b (line 18) | def b():
function c (line 22) | def c():
FILE: tests/exceptions/source/backtrace/chaining_second.py
function a_decorator (line 9) | def a_decorator():
function a_context_manager (line 13) | def a_context_manager():
function a_explicit (line 18) | def a_explicit():
function b_decorated (line 26) | def b_decorated():
function b_not_decorated (line 30) | def b_not_decorated():
function c (line 34) | def c():
FILE: tests/exceptions/source/backtrace/chaining_third.py
function a_decorator (line 9) | def a_decorator():
function a_context_manager (line 13) | def a_context_manager():
function a_explicit (line 17) | def a_explicit():
function b_decorator (line 21) | def b_decorator():
function b_context_manager (line 25) | def b_context_manager():
function b_explicit (line 30) | def b_explicit():
function c_decorated (line 38) | def c_decorated():
function c_not_decorated (line 42) | def c_not_decorated():
FILE: tests/exceptions/source/backtrace/enqueue_with_others_handlers.py
function check_tb_sink (line 6) | def check_tb_sink(message):
FILE: tests/exceptions/source/backtrace/frame_values_backward.py
function a (line 12) | def a(n):
function b (line 16) | def b(n):
function c (line 20) | def c(n):
FILE: tests/exceptions/source/backtrace/frame_values_forward.py
function a (line 11) | def a(n):
function b (line 15) | def b(n):
function c (line 20) | def c(n):
FILE: tests/exceptions/source/backtrace/function.py
function a (line 10) | def a():
function b (line 14) | def b():
function c (line 18) | def c():
FILE: tests/exceptions/source/backtrace/head_recursion.py
function a (line 10) | def a(n):
function b (line 16) | def b(n):
function c (line 23) | def c(n):
FILE: tests/exceptions/source/backtrace/missing_attributes_traceback_objects.py
function div (line 13) | def div(x, y):
function foo (line 17) | def foo():
function make_fake (line 28) | def make_fake(tb):
FILE: tests/exceptions/source/backtrace/missing_lineno_frame_objects.py
function make_fake (line 22) | def make_fake(tb):
function a (line 31) | def a():
function b (line 35) | def b():
FILE: tests/exceptions/source/backtrace/nested.py
function a (line 9) | def a(x):
function b (line 20) | def b(x):
function c (line 31) | def c(x):
FILE: tests/exceptions/source/backtrace/nested_chained_catch_up.py
function foo (line 10) | def foo():
function bar (line 15) | def bar():
function main (line 20) | def main():
FILE: tests/exceptions/source/backtrace/nested_decorator_catch_up.py
function foo (line 11) | def foo():
function bar (line 16) | def bar():
FILE: tests/exceptions/source/backtrace/nested_explicit_catch_up.py
function foo (line 10) | def foo():
function bar (line 15) | def bar():
FILE: tests/exceptions/source/backtrace/nested_wrapping.py
function f (line 9) | def f(i):
function a (line 15) | def a(x):
FILE: tests/exceptions/source/backtrace/no_tb.py
function f (line 9) | def f():
FILE: tests/exceptions/source/backtrace/not_enough_arguments.py
function decorated (line 10) | def decorated(x, y, z):
function not_decorated (line 14) | def not_decorated(x, y, z):
FILE: tests/exceptions/source/backtrace/raising_recursion.py
function a (line 10) | def a(n):
function b (line 16) | def b(n):
function c (line 23) | def c(n):
FILE: tests/exceptions/source/backtrace/suppressed_expression_direct.py
function a (line 9) | def a(x, y):
function b_decorated (line 14) | def b_decorated():
function b_not_decorated (line 21) | def b_not_decorated():
function c_decorator (line 28) | def c_decorator():
function c_context_manager (line 32) | def c_context_manager():
function c_explicit (line 37) | def c_explicit():
FILE: tests/exceptions/source/backtrace/suppressed_expression_indirect.py
function a (line 9) | def a(x, y):
function b (line 13) | def b():
function c_decorated (line 21) | def c_decorated():
function c_not_decorated (line 25) | def c_not_decorated():
FILE: tests/exceptions/source/backtrace/tail_recursion.py
function a (line 10) | def a(n):
function b (line 15) | def b(n):
function c (line 21) | def c(n):
FILE: tests/exceptions/source/backtrace/too_many_arguments.py
function decorated (line 10) | def decorated():
function not_decorated (line 14) | def not_decorated():
FILE: tests/exceptions/source/diagnose/assertion_error.py
function foo (line 9) | def foo(abc, xyz):
FILE: tests/exceptions/source/diagnose/assertion_error_custom.py
function foo (line 9) | def foo(abc, xyz):
FILE: tests/exceptions/source/diagnose/assertion_error_in_string.py
function foo (line 9) | def foo(abc, xyz):
FILE: tests/exceptions/source/diagnose/attributes.py
class Obj (line 10) | class Obj:
method forbidden (line 12) | def forbidden(self):
function foo (line 20) | def foo():
FILE: tests/exceptions/source/diagnose/chained_both.py
function div (line 9) | def div(x, y):
function cause (line 13) | def cause(x, y):
function context (line 20) | def context(x, y):
FILE: tests/exceptions/source/diagnose/encoding.py
function _deep (line 9) | def _deep(val):
function div (line 13) | def div():
FILE: tests/exceptions/source/diagnose/global_variable.py
function func (line 13) | def func():
FILE: tests/exceptions/source/diagnose/keyword_argument.py
function f (line 9) | def f(x):
FILE: tests/exceptions/source/diagnose/multilines_repr.py
class A (line 9) | class A:
method __repr__ (line 10) | def __repr__(self):
function multiline (line 14) | def multiline():
FILE: tests/exceptions/source/diagnose/no_error_message.py
function foo (line 9) | def foo():
function bar (line 13) | def bar():
FILE: tests/exceptions/source/diagnose/parenthesis.py
class XYZ (line 10) | class XYZ:
function a (line 14) | def a(b, c):
function b (line 20) | def b():
function c (line 25) | def c():
function d (line 31) | def d(j):
function e (line 40) | def e():
FILE: tests/exceptions/source/diagnose/source_multilines.py
function bug_1 (line 10) | def bug_1(n):
function bug_2 (line 15) | def bug_2(a, b, c):
function bug_3 (line 20) | def bug_3(string):
function bug_4 (line 25) | def bug_4():
FILE: tests/exceptions/source/diagnose/syntax_highlighting.py
function a (line 10) | def a():
function b (line 14) | def b():
function c (line 18) | def c():
function d (line 22) | def d():
function e (line 26) | def e(x):
FILE: tests/exceptions/source/diagnose/truncating.py
function div (line 9) | def div():
FILE: tests/exceptions/source/diagnose/unprintable_object.py
class Object (line 9) | class Object:
method __repr__ (line 10) | def __repr__(self):
FILE: tests/exceptions/source/modern/decorate_async_generator.py
function test_decorate_async_generator (line 11) | def test_decorate_async_generator():
function test_decorate_async_generator_with_error (line 27) | def test_decorate_async_generator_with_error():
function test_decorate_async_generator_with_error_reraised (line 43) | def test_decorate_async_generator_with_error_reraised():
function test_decorate_async_generator_then_async_send (line 65) | def test_decorate_async_generator_then_async_send():
function test_decorate_async_generator_then_async_throw (line 85) | def test_decorate_async_generator_then_async_throw():
FILE: tests/exceptions/source/modern/exception_formatting_async_generator.py
function foo (line 13) | async def foo(a, b):
FILE: tests/exceptions/source/modern/exception_group_catch.py
function a (line 12) | def a():
function b (line 18) | def b():
FILE: tests/exceptions/source/modern/f_string.py
function hello (line 10) | def hello():
function world (line 14) | def world():
FILE: tests/exceptions/source/modern/grouped_as_cause_and_context.py
function a (line 9) | def a():
function b (line 13) | def b():
function main (line 18) | def main():
FILE: tests/exceptions/source/modern/grouped_max_depth.py
function main (line 6) | def main():
FILE: tests/exceptions/source/modern/grouped_max_length.py
function main (line 6) | def main():
FILE: tests/exceptions/source/modern/grouped_nested.py
function divide_by_zero (line 5) | def divide_by_zero():
function raise_value_error (line 9) | def raise_value_error(value):
function main (line 14) | def main():
FILE: tests/exceptions/source/modern/grouped_simple.py
function a (line 9) | def a():
function b (line 15) | def b():
function c (line 19) | def c(f):
function main (line 24) | def main():
FILE: tests/exceptions/source/modern/grouped_with_cause_and_context.py
function a (line 9) | def a():
function main (line 14) | def main():
FILE: tests/exceptions/source/modern/match_statement.py
function case (line 9) | def case(x):
function match (line 15) | def match(x):
FILE: tests/exceptions/source/modern/positional_only_argument.py
function foo (line 10) | def foo(a, /, b, *, c, **d): 1 / 0
function main (line 13) | def main():
FILE: tests/exceptions/source/modern/type_hints.py
function foo (line 15) | def foo(a: int, b: Tuple[Name, float], c: "Name") -> T: 1 / 0
function main (line 18) | def main():
FILE: tests/exceptions/source/modern/walrus_operator.py
function foo (line 7) | def foo():
function bar (line 12) | def bar():
function main (line 17) | def main():
FILE: tests/exceptions/source/others/assertionerror_without_traceback.py
function test (line 6) | def test(diagnose, backtrace):
FILE: tests/exceptions/source/others/broken_but_decorated_repr.py
class Foo (line 9) | class Foo:
method __repr__ (line 11) | def __repr__(self):
class Bar (line 15) | class Bar:
method __repr__ (line 16) | def __repr__(self):
FILE: tests/exceptions/source/others/catch_as_decorator_with_parentheses.py
function c (line 10) | def c(a, b):
FILE: tests/exceptions/source/others/catch_as_decorator_without_parentheses.py
function c (line 10) | def c(a, b=0):
FILE: tests/exceptions/source/others/catch_as_function.py
function a (line 9) | def a():
FILE: tests/exceptions/source/others/catch_message.py
function a (line 9) | def a():
FILE: tests/exceptions/source/others/exception_formatting_coroutine.py
function foo (line 13) | async def foo(a, b):
FILE: tests/exceptions/source/others/exception_formatting_function.py
function a (line 13) | def a(a, b):
FILE: tests/exceptions/source/others/exception_formatting_generator.py
function foo (line 13) | def foo(a, b):
FILE: tests/exceptions/source/others/exception_in_property.py
class A (line 9) | class A:
method value (line 11) | def value(self):
FILE: tests/exceptions/source/others/handler_formatting_with_context_manager.py
function a (line 15) | def a():
FILE: tests/exceptions/source/others/handler_formatting_with_decorator.py
function a (line 16) | def a():
FILE: tests/exceptions/source/others/level_name.py
function a (line 11) | def a():
FILE: tests/exceptions/source/others/level_number.py
function a (line 11) | def a():
FILE: tests/exceptions/source/others/message_formatting_with_context_manager.py
function a (line 9) | def a():
FILE: tests/exceptions/source/others/message_formatting_with_decorator.py
function a (line 10) | def a():
FILE: tests/exceptions/source/others/nested_with_reraise.py
function foo (line 13) | def foo(a, b):
function bar (line 18) | def bar(x, y):
function baz (line 25) | def baz():
FILE: tests/exceptions/source/others/recursion_error.py
function recursive (line 14) | def recursive():
FILE: tests/exceptions/source/others/repeated_lines.py
function recursive (line 13) | def recursive(outer, inner):
FILE: tests/exceptions/source/others/syntaxerror_without_traceback.py
function test (line 6) | def test(diagnose, backtrace):
FILE: tests/exceptions/source/others/sys_tracebacklimit.py
function a (line 12) | def a():
function b (line 16) | def b():
function c (line 20) | def c():
function d (line 24) | def d():
function e (line 28) | def e():
function f (line 32) | def f():
function g (line 36) | def g():
function h (line 40) | def h():
function i (line 44) | def i():
function j (line 48) | def j(a, b):
FILE: tests/exceptions/source/others/sys_tracebacklimit_negative.py
function a (line 12) | def a():
function b (line 16) | def b():
function c (line 20) | def c():
function d (line 24) | def d():
function e (line 28) | def e():
function f (line 32) | def f():
function g (line 36) | def g():
function h (line 40) | def h():
function i (line 44) | def i():
function j (line 48) | def j(a, b):
FILE: tests/exceptions/source/others/sys_tracebacklimit_none.py
function a (line 12) | def a():
function b (line 16) | def b():
function c (line 20) | def c():
function d (line 24) | def d():
function e (line 28) | def e():
function f (line 32) | def f():
function g (line 36) | def g():
function h (line 40) | def h():
function i (line 44) | def i():
function j (line 48) | def j(a, b):
FILE: tests/exceptions/source/others/sys_tracebacklimit_unset.py
function a (line 12) | def a():
function b (line 16) | def b():
function c (line 20) | def c():
function d (line 24) | def d():
function e (line 28) | def e():
function f (line 32) | def f():
function g (line 36) | def g():
function h (line 40) | def h():
function i (line 44) | def i():
function j (line 48) | def j(a, b):
FILE: tests/exceptions/source/others/zerodivisionerror_without_traceback.py
function test (line 6) | def test(diagnose, backtrace):
FILE: tests/exceptions/source/ownership/assertion_from_lib.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/assertion_from_local.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/callback.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/catch_decorator.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/catch_decorator_from_lib.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/decorated_callback.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/direct.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/indirect.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/string_lib.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/string_source.py
function test (line 6) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/syntaxerror.py
function test (line 9) | def test(*, backtrace, colorize, diagnose):
FILE: tests/exceptions/source/ownership/usersite/somelib/__init__.py
function divide (line 1) | def divide(x, y):
function divide_indirect (line 5) | def divide_indirect(a, b):
function callme (line 9) | def callme(callback):
function execute (line 13) | def execute():
function syntaxerror (line 17) | def syntaxerror():
function assertionerror (line 21) | def assertionerror(x, y):
FILE: tests/test_activation.py
function test_disable (line 20) | def test_disable(writer, name, should_log):
function test_enable (line 46) | def test_enable(writer, name, should_log):
function test_log_before_enable (line 59) | def test_log_before_enable(writer):
function test_log_before_disable (line 69) | def test_log_before_disable(writer):
function test_multiple_activations (line 79) | def test_multiple_activations():
function test_log_before_enable_incomplete_frame_context (line 110) | def test_log_before_enable_incomplete_frame_context(writer, incomplete_f...
function test_log_before_disable_incomplete_frame_context (line 120) | def test_log_before_disable_incomplete_frame_context(writer, incomplete_...
function test_incomplete_frame_context_with_others (line 130) | def test_incomplete_frame_context_with_others(writer, incomplete_frame_c...
function test_invalid_enable_name (line 145) | def test_invalid_enable_name(name):
function test_invalid_disable_name (line 151) | def test_invalid_disable_name(name):
FILE: tests/test_add_option_backtrace.py
function test_backtrace (line 6) | def test_backtrace(writer):
FILE: tests/test_add_option_catch.py
function broken_sink (line 12) | def broken_sink(m):
function test_catch_is_true (line 16) | def test_catch_is_true(capsys):
function test_catch_is_false (line 24) | def test_catch_is_false(capsys):
function test_no_sys_stderr (line 32) | def test_no_sys_stderr(capsys, monkeypatch):
function test_broken_sys_stderr (line 42) | def test_broken_sys_stderr(capsys, monkeypatch):
function test_encoding_error (line 55) | def test_encoding_error(capsys):
function test_unprintable_record (line 73) | def test_unprintable_record(writer, capsys):
function test_broken_sink_message (line 95) | def test_broken_sink_message(capsys, enqueue):
function test_broken_sink_caught_keep_working (line 111) | def test_broken_sink_caught_keep_working(enqueue):
function test_broken_sink_not_caught_enqueue (line 129) | def test_broken_sink_not_caught_enqueue():
FILE: tests/test_add_option_colorize.py
function test_colorized_format (line 19) | def test_colorized_format(format, message, expected, writer):
function test_decolorized_format (line 34) | def test_decolorized_format(format, message, expected, writer):
function test_colorize_stream (line 43) | def test_colorize_stream(stream):
function test_decolorize_stream (line 52) | def test_decolorize_stream(stream):
function test_automatic_detection_when_stream_is_a_tty (line 59) | def test_automatic_detection_when_stream_is_a_tty():
function test_automatic_detection_when_stream_is_not_a_tty (line 66) | def test_automatic_detection_when_stream_is_not_a_tty():
function test_automatic_detection_when_stream_has_no_isatty (line 73) | def test_automatic_detection_when_stream_has_no_isatty():
function test_override_no_color (line 80) | def test_override_no_color(monkeypatch):
function test_override_force_color (line 89) | def test_override_force_color(monkeypatch):
FILE: tests/test_add_option_context.py
function test_using_multiprocessing_directly_if_context_is_none (line 10) | def test_using_multiprocessing_directly_if_context_is_none():
function test_fork_context_as_string (line 17) | def test_fork_context_as_string(context_name):
function test_spawn_context_as_string (line 25) | def test_spawn_context_as_string():
function test_fork_context_as_object (line 35) | def test_fork_context_as_object(context_name):
function test_spawn_context_as_object (line 43) | def test_spawn_context_as_object():
function test_global_start_method_is_none_if_enqueue_is_false (line 51) | def test_global_start_method_is_none_if_enqueue_is_false():
function test_invalid_context_name (line 56) | def test_invalid_context_name():
function test_invalid_context_object (line 62) | def test_invalid_context_object(context):
FILE: tests/test_add_option_diagnose.py
function test_diagnose (line 6) | def test_diagnose(writer):
FILE: tests/test_add_option_enqueue.py
class NotPicklable (line 13) | class NotPicklable:
method __getstate__ (line 14) | def __getstate__(self):
method __setstate__ (line 17) | def __setstate__(self, state):
class NotPicklableTypeError (line 21) | class NotPicklableTypeError:
method __getstate__ (line 22) | def __getstate__(self):
method __setstate__ (line 25) | def __setstate__(self, state):
class NotUnpicklable (line 29) | class NotUnpicklable:
method __getstate__ (line 30) | def __getstate__(self):
method __setstate__ (line 33) | def __setstate__(self, state):
class NotUnpicklableTypeError (line 37) | class NotUnpicklableTypeError:
method __getstate__ (line 38) | def __getstate__(self):
method __setstate__ (line 41) | def __setstate__(self, state):
class NotWritable (line 45) | class NotWritable:
method write (line 46) | def write(self, message):
function test_enqueue (line 52) | def test_enqueue():
function test_enqueue_with_exception (line 67) | def test_enqueue_with_exception():
function test_caught_exception_queue_put (line 90) | def test_caught_exception_queue_put(writer, capsys):
function test_caught_exception_queue_get (line 108) | def test_caught_exception_queue_get(writer, capsys):
function test_caught_exception_sink_write (line 126) | def test_caught_exception_sink_write(capsys):
function test_not_caught_exception_queue_put (line 143) | def test_not_caught_exception_queue_put(writer, capsys):
function test_not_caught_exception_queue_get (line 159) | def test_not_caught_exception_queue_get(writer, capsys):
function test_not_caught_exception_sink_write (line 178) | def test_not_caught_exception_sink_write(capsys):
function test_not_caught_exception_sink_write_then_complete (line 196) | def test_not_caught_exception_sink_write_then_complete(capsys):
function test_not_caught_exception_queue_get_then_complete (line 214) | def test_not_caught_exception_queue_get_then_complete(writer, capsys):
function test_wait_for_all_messages_enqueued (line 233) | def test_wait_for_all_messages_enqueued(capsys):
function test_logging_not_picklable_exception (line 252) | def test_logging_not_picklable_exception(exception_value):
function test_logging_not_unpicklable_exception (line 275) | def test_logging_not_unpicklable_exception(exception_value):
FILE: tests/test_add_option_filter.py
function test_filtered_in (line 28) | def test_filtered_in(filter, writer):
function test_filtered_out (line 52) | def test_filtered_out(filter, writer):
function test_filtered_in_incomplete_frame_context (line 71) | def test_filtered_in_incomplete_frame_context(writer, filter, incomplete...
function test_filtered_out_incomplete_frame_context (line 88) | def test_filtered_out_incomplete_frame_context(writer, filter, incomplet...
function test_invalid_filter (line 95) | def test_invalid_filter(writer, filter):
function test_invalid_filter_dict_level_types (line 101) | def test_invalid_filter_dict_level_types(writer, filter):
function test_invalid_filter_dict_module_types (line 107) | def test_invalid_filter_dict_module_types(writer, filter):
function test_invalid_filter_dict_values_unknown_level (line 113) | def test_invalid_filter_dict_values_unknown_level(writer, filter):
function test_invalid_filter_dict_values_wrong_integer_value (line 124) | def test_invalid_filter_dict_values_wrong_integer_value(writer):
function test_filter_dict_with_custom_level (line 135) | def test_filter_dict_with_custom_level(writer):
function test_invalid_filter_builtin (line 143) | def test_invalid_filter_builtin(writer):
FILE: tests/test_add_option_format.py
function test_format (line 17) | def test_format(message, format, expected, writer):
function test_progressive_format (line 23) | def test_progressive_format(writer):
function test_function_format_without_exception (line 39) | def test_function_format_without_exception(writer):
function test_function_format_with_exception (line 48) | def test_function_format_with_exception(writer):
function test_invalid_format (line 60) | def test_invalid_format(writer, format):
function test_invalid_markups (line 66) | def test_invalid_markups(writer, format):
function test_markup_in_field (line 74) | def test_markup_in_field(writer, colorize):
function test_invalid_format_builtin (line 85) | def test_invalid_format_builtin(writer):
FILE: tests/test_add_option_kwargs.py
function test_file_mode_a (line 6) | def test_file_mode_a(tmp_path):
function test_file_mode_w (line 14) | def test_file_mode_w(tmp_path):
function test_file_auto_buffering (line 22) | def test_file_auto_buffering(tmp_path):
function test_file_line_buffering (line 42) | def test_file_line_buffering(tmp_path):
function test_invalid_function_kwargs (line 51) | def test_invalid_function_kwargs():
function test_invalid_file_object_kwargs (line 59) | def test_invalid_file_object_kwargs():
function test_invalid_file_kwargs (line 73) | def test_invalid_file_kwargs():
function test_invalid_coroutine_kwargs (line 78) | def test_invalid_coroutine_kwargs():
FILE: tests/test_add_option_level.py
function test_level_low_enough (line 7) | def test_level_low_enough(writer, level):
function test_level_too_high (line 14) | def test_level_too_high(writer, level):
function test_invalid_level_type (line 21) | def test_invalid_level_type(writer, level):
function test_invalid_level_value (line 26) | def test_invalid_level_value(writer):
function test_unknown_level (line 33) | def test_unknown_level(writer):
FILE: tests/test_add_option_serialize.py
class JsonSink (line 8) | class JsonSink:
method __init__ (line 9) | def __init__(self):
method write (line 14) | def write(self, message):
function test_serialize (line 20) | def test_serialize():
function test_serialize_non_ascii_characters (line 29) | def test_serialize_non_ascii_characters():
function test_serialize_exception (line 40) | def test_serialize_exception():
function test_serialize_exception_without_context (line 60) | def test_serialize_exception_without_context():
function test_serialize_exception_none_tuple (line 77) | def test_serialize_exception_none_tuple():
function test_serialize_exception_instance (line 94) | def test_serialize_exception_instance():
function test_serialize_with_catch_decorator (line 111) | def test_serialize_with_catch_decorator():
function test_serialize_with_record_option (line 127) | def test_serialize_with_record_option():
function test_serialize_not_serializable (line 137) | def test_serialize_not_serializable():
FILE: tests/test_add_sinks.py
function log (line 17) | def log(sink, rep=1):
function async_log (line 26) | async def async_log(sink, rep=1):
function test_stdout_sink (line 37) | def test_stdout_sink(rep, capsys):
function test_stderr_sink (line 45) | def test_stderr_sink(rep, capsys):
function test_devnull (line 53) | def test_devnull(rep):
function test_file_path_sink (line 59) | def test_file_path_sink(rep, path_type, tmp_path):
function test_file_opened_sink (line 67) | def test_file_opened_sink(rep, tmp_path):
function test_file_sink_folder_creation (line 75) | def test_file_sink_folder_creation(rep, tmp_path):
function test_function_sink (line 82) | def test_function_sink(rep):
function test_coroutine_sink (line 93) | def test_coroutine_sink(capsys, rep):
function test_file_object_sink (line 107) | def test_file_object_sink(rep):
function test_standard_handler_sink (line 121) | def test_standard_handler_sink(rep):
function test_flush (line 134) | def test_flush(rep):
function test_file_sink_ascii_encoding (line 149) | def test_file_sink_ascii_encoding(tmp_path):
function test_file_sink_utf8_encoding (line 157) | def test_file_sink_utf8_encoding(tmp_path):
function test_file_sink_default_encoding (line 165) | def test_file_sink_default_encoding(tmp_path):
function test_disabled_logger_in_sink (line 173) | def test_disabled_logger_in_sink(sink_with_logger):
function test_custom_sink_invalid_flush (line 182) | def test_custom_sink_invalid_flush(capsys, flush):
function test_custom_sink_invalid_stop (line 199) | def test_custom_sink_invalid_stop(capsys, stop):
function test_custom_sink_invalid_complete (line 217) | def test_custom_sink_invalid_complete(capsys, complete):
function test_invalid_sink (line 238) | def test_invalid_sink(sink):
function test_deprecated_start_and_stop (line 243) | def test_deprecated_start_and_stop(writer):
FILE: tests/test_ansimarkup_basic.py
function test_styles (line 18) | def test_styles(text, expected):
function test_background_colors (line 31) | def test_background_colors(text, expected):
function test_foreground_colors (line 44) | def test_foreground_colors(text, expected):
function test_nested (line 91) | def test_nested(text, expected):
function test_strict_parsing (line 96) | def test_strict_parsing(text):
function test_permissive_parsing (line 111) | def test_permissive_parsing(text, expected):
function test_autoclose (line 138) | def test_autoclose(text, expected):
function test_escaping (line 156) | def test_escaping(text, expected):
function test_mismatched_error (line 174) | def test_mismatched_error(text, strip):
function test_unbalanced_error (line 185) | def test_unbalanced_error(text, strip):
function test_unclosed_error (line 192) | def test_unclosed_error(text, strip):
function test_invalid_color (line 217) | def test_invalid_color(text, strip):
function test_strip (line 238) | def test_strip(text, expected):
FILE: tests/test_ansimarkup_extended.py
function test_background_colors (line 16) | def test_background_colors(text, expected):
function test_foreground_colors (line 29) | def test_foreground_colors(text, expected):
function test_8bit_colors (line 41) | def test_8bit_colors(text, expected):
function test_hex_colors (line 54) | def test_hex_colors(text, expected):
function test_hex_short_code_equals_long_code (line 70) | def test_hex_short_code_equals_long_code(layer, short_code, long_code):
function test_rgb_colors (line 83) | def test_rgb_colors(text, expected):
function test_nested (line 115) | def test_nested(text, expected):
function test_tricky_parse (line 135) | def test_tricky_parse(text, expected):
function test_invalid_color (line 155) | def test_invalid_color(text, strip):
function test_invalid_hex (line 177) | def test_invalid_hex(text, strip):
function test_invalid_8bit (line 190) | def test_invalid_8bit(text, strip):
function test_invalid_rgb (line 212) | def test_invalid_rgb(text, strip):
function test_strip (line 231) | def test_strip(text, expected):
function test_tricky_strip (line 251) | def test_tricky_strip(text, expected):
FILE: tests/test_bind.py
function test_bind_after_add (line 6) | def test_bind_after_add(writer):
function test_bind_before_add (line 14) | def test_bind_before_add(writer):
function test_add_using_bound (line 22) | def test_add_using_bound(writer):
function test_not_override_parent_logger (line 32) | def test_not_override_parent_logger(writer):
function test_override_previous_bound (line 43) | def test_override_previous_bound(writer):
function test_no_conflict (line 49) | def test_no_conflict(writer):
function test_bind_and_add_level (line 63) | def test_bind_and_add_level(writer, using_bound):
function test_override_configured (line 78) | def test_override_configured(writer):
FILE: tests/test_colorama.py
function clear_environment (line 20) | def clear_environment():
function patch_colorama (line 28) | def patch_colorama(monkeypatch):
function test_stream_wrapped_on_windows_if_no_vt_support (line 44) | def test_stream_wrapped_on_windows_if_no_vt_support(patched, monkeypatch...
function test_stream_not_wrapped_on_windows_if_vt_support (line 56) | def test_stream_not_wrapped_on_windows_if_vt_support(patched, monkeypatc...
function test_stream_is_none (line 66) | def test_stream_is_none():
function test_is_a_tty (line 70) | def test_is_a_tty():
function test_is_not_a_tty (line 74) | def test_is_not_a_tty():
function test_is_a_tty_exception (line 78) | def test_is_a_tty_exception():
function test_pycharm_fixed (line 92) | def test_pycharm_fixed(monkeypatch, patched, expected):
function test_github_actions_fixed (line 110) | def test_github_actions_fixed(monkeypatch, patched, expected):
function test_mintty_fixed_windows (line 130) | def test_mintty_fixed_windows(monkeypatch, patched, expected):
function test_dumb_term_not_colored (line 148) | def test_dumb_term_not_colored(monkeypatch, patched, expected):
function test_honor_no_color_standard (line 173) | def test_honor_no_color_standard(monkeypatch, patched, no_color, expected):
function test_honor_force_color_standard (line 198) | def test_honor_force_color_standard(monkeypatch, patched, force_color, e...
function test_no_color_takes_precedence_over_force_color (line 206) | def test_no_color_takes_precedence_over_force_color(monkeypatch):
function test_mintty_not_fixed_linux (line 232) | def test_mintty_not_fixed_linux(monkeypatch, patched, expected):
function test_jupyter_fixed (line 252) | def test_jupyter_fixed(monkeypatch, patched, out_class, expected):
function test_jupyter_missing_lib (line 274) | def test_jupyter_missing_lib(monkeypatch):
function test_dont_wrap_on_linux (line 285) | def test_dont_wrap_on_linux(monkeypatch, patched, patch_colorama):
function test_dont_wrap_if_not_original_stdout_or_stderr (line 295) | def test_dont_wrap_if_not_original_stdout_or_stderr(monkeypatch, patched...
function test_dont_wrap_if_terminal_has_vt_support (line 305) | def test_dont_wrap_if_terminal_has_vt_support(monkeypatch, patched, patc...
function test_dont_wrap_if_winapi_false (line 317) | def test_dont_wrap_if_winapi_false(monkeypatch, patched, patch_colorama):
function test_wrap_if_winapi_true_and_no_vt_support (line 329) | def test_wrap_if_winapi_true_and_no_vt_support(monkeypatch, patched, pat...
function test_wrap_if_winapi_true_and_vt_check_fails (line 342) | def test_wrap_if_winapi_true_and_vt_check_fails(monkeypatch, patched, pa...
function test_wrap_if_winapi_true_and_stream_has_no_fileno (line 355) | def test_wrap_if_winapi_true_and_stream_has_no_fileno(monkeypatch, patch...
function test_wrap_if_winapi_true_and_old_colorama_version (line 367) | def test_wrap_if_winapi_true_and_old_colorama_version(monkeypatch, patch...
FILE: tests/test_configure.py
function test_handlers (line 8) | def test_handlers(capsys, tmp_path):
function test_levels (line 26) | def test_levels(writer):
function test_extra (line 38) | def test_extra(writer):
function test_patcher (line 49) | def test_patcher(writer):
function test_activation (line 58) | def test_activation(writer):
function test_dict_unpacking (line 69) | def test_dict_unpacking(writer):
function test_returned_ids (line 85) | def test_returned_ids(capsys):
function test_dont_reset_by_default (line 113) | def test_dont_reset_by_default(writer):
function test_reset_previous_handlers (line 125) | def test_reset_previous_handlers(writer):
function test_reset_previous_extra (line 135) | def test_reset_previous_extra(writer):
function test_reset_previous_patcher (line 145) | def test_reset_previous_patcher(writer):
function test_dont_reset_previous_levels (line 155) | def test_dont_reset_previous_levels(writer):
function test_configure_handler_using_new_level (line 167) | def test_configure_handler_using_new_level(writer):
function test_configure_filter_using_new_level (line 179) | def test_configure_filter_using_new_level(writer):
function test_configure_before_bind (line 191) | def test_configure_before_bind(writer):
function test_configure_after_bind (line 206) | def test_configure_after_bind(writer):
FILE: tests/test_contextualize.py
function test_contextualize (line 12) | def test_contextualize(writer):
function test_contextualize_as_decorator (line 21) | def test_contextualize_as_decorator(writer):
function test_contextualize_in_function (line 33) | def test_contextualize_in_function(writer):
function test_contextualize_reset (line 45) | def test_contextualize_reset():
function test_contextualize_async (line 68) | def test_contextualize_async(writer):
function test_contextualize_thread (line 92) | def test_contextualize_thread(writer):
function test_contextualize_before_bind (line 120) | def test_contextualize_before_bind(writer):
function test_contextualize_after_bind (line 134) | def test_contextualize_after_bind(writer):
function test_contextualize_using_bound (line 147) | def test_contextualize_using_bound(writer):
function test_contextualize_before_configure (line 161) | def test_contextualize_before_configure(writer):
function test_contextualize_after_configure (line 174) | def test_contextualize_after_configure(writer):
function test_nested_contextualize (line 186) | def test_nested_contextualize(writer):
function test_context_reset_despite_error (line 201) | def test_context_reset_despite_error(writer):
function test_contextvars_fallback_352 (line 216) | def test_contextvars_fallback_352(monkeypatch):
FILE: tests/test_coroutine_sink.py
function async_writer (line 15) | async def async_writer(msg):
class AsyncWriter (line 20) | class AsyncWriter:
method __call__ (line 21) | async def __call__(self, msg):
function test_coroutine_function (line 26) | def test_coroutine_function(capsys):
function test_async_callable_sink (line 40) | def test_async_callable_sink(capsys):
function test_concurrent_execution (line 54) | def test_concurrent_execution(capsys):
function test_recursive_coroutine (line 72) | def test_recursive_coroutine(capsys):
function test_using_another_event_loop (line 90) | def test_using_another_event_loop(capsys):
function test_run_multiple_different_loops (line 105) | def test_run_multiple_different_loops(capsys):
function test_run_multiple_same_loop (line 121) | def test_run_multiple_same_loop(capsys):
function test_using_sink_without_running_loop_not_none (line 137) | def test_using_sink_without_running_loop_not_none(capsys):
function test_using_sink_without_running_loop_none (line 151) | def test_using_sink_without_running_loop_none(capsys):
function test_global_loop_not_used (line 166) | def test_global_loop_not_used(capsys):
function test_complete_in_another_run (line 182) | def test_complete_in_another_run(capsys):
function test_tasks_cancelled_on_remove (line 201) | def test_tasks_cancelled_on_remove(capsys):
function test_remove_without_tasks (line 217) | def test_remove_without_tasks(capsys):
function test_complete_without_tasks (line 231) | def test_complete_without_tasks(capsys):
function test_complete_stream_noop (line 243) | def test_complete_stream_noop(capsys):
function test_complete_file_noop (line 261) | def test_complete_file_noop(tmp_path):
function test_complete_function_noop (line 279) | def test_complete_function_noop():
function test_complete_standard_noop (line 301) | def test_complete_standard_noop(capsys):
function test_exception_in_coroutine_caught (line 319) | def test_exception_in_coroutine_caught(capsys):
function test_exception_in_coroutine_not_caught (line 341) | def test_exception_in_coroutine_not_caught(capsys, caplog):
function test_exception_in_coroutine_during_complete_caught (line 369) | def test_exception_in_coroutine_during_complete_caught(capsys):
function test_exception_in_coroutine_during_complete_not_caught (line 391) | def test_exception_in_coroutine_during_complete_not_caught(capsys, caplog):
function test_enqueue_coroutine_loop (line 420) | def test_enqueue_coroutine_loop(capsys):
function test_enqueue_coroutine_from_inside_coroutine_without_loop (line 435) | def test_enqueue_coroutine_from_inside_coroutine_without_loop(capsys):
function test_custom_complete_function (line 450) | def test_custom_complete_function(capsys):
function test_complete_from_another_loop (line 477) | def test_complete_from_another_loop(capsys, loop_is_none):
function test_complete_from_multiple_threads_loop_is_none (line 501) | def test_complete_from_multiple_threads_loop_is_none(capsys):
function test_complete_from_multiple_threads_loop_is_not_none (line 529) | def test_complete_from_multiple_threads_loop_is_not_none(capsys):
function test_complete_and_sink_write_concurrency (line 563) | def test_complete_and_sink_write_concurrency():
function test_complete_and_contextualize_concurrency (line 592) | def test_complete_and_contextualize_concurrency():
function async_subworker (line 624) | async def async_subworker(logger_):
function async_mainworker (line 629) | async def async_mainworker(logger_):
function subworker (line 634) | def subworker(logger_):
class Writer (line 639) | class Writer:
method __init__ (line 640) | def __init__(self):
method write (line 643) | async def write(self, message):
function test_complete_with_sub_processes (line 647) | def test_complete_with_sub_processes(capsys):
function test_invalid_coroutine_sink_if_no_loop_with_enqueue (line 669) | def test_invalid_coroutine_sink_if_no_loop_with_enqueue():
FILE: tests/test_datetime.py
function _expected_fallback_time_zone (line 19) | def _expected_fallback_time_zone():
function test_formatting (line 130) | def test_formatting(writer, freeze_time, time_format, date, timezone, ex...
function test_formatting_timezone_offset_down_to_the_second (line 147) | def test_formatting_timezone_offset_down_to_the_second(
function test_locale_formatting (line 158) | def test_locale_formatting(writer, freeze_time):
function test_stdout_formatting (line 166) | def test_stdout_formatting(freeze_time, capsys):
function test_file_formatting (line 175) | def test_file_formatting(freeze_time, tmp_path):
function test_missing_struct_time_fields (line 182) | def test_missing_struct_time_fields(writer, freeze_time):
function test_value_of_gmtoff_is_invalid (line 194) | def test_value_of_gmtoff_is_invalid(writer, freeze_time, tm_gmtoff):
function test_localtime_raising_exception (line 206) | def test_localtime_raising_exception(writer, freeze_time, monkeypatch, e...
function test_update_with_zone_info (line 232) | def test_update_with_zone_info(writer, freeze_time, date, expected_result):
function test_freezegun_mocking (line 247) | def test_freezegun_mocking(writer):
function test_invalid_time_format (line 259) | def test_invalid_time_format(writer, time_format):
FILE: tests/test_deepcopy.py
function print_ (line 6) | def print_(message):
function test_add_sink_after_deepcopy (line 10) | def test_add_sink_after_deepcopy(capsys):
function test_add_sink_before_deepcopy (line 23) | def test_add_sink_before_deepcopy(capsys):
function test_remove_from_original (line 36) | def test_remove_from_original(capsys):
function test_remove_from_copy (line 50) | def test_remove_from_copy(capsys):
FILE: tests/test_defaults.py
function test_string (line 7) | def test_string(value, monkeypatch):
function test_bool_positive (line 15) | def test_bool_positive(value, monkeypatch):
function test_bool_negative (line 23) | def test_bool_negative(value, monkeypatch):
function test_int (line 30) | def test_int(monkeypatch):
function test_invalid_int (line 38) | def test_invalid_int(value, monkeypatch):
function test_invalid_bool (line 50) | def test_invalid_bool(value, monkeypatch):
function test_invalid_type (line 61) | def test_invalid_type(monkeypatch):
FILE: tests/test_exceptions_catch.py
function test_caret_not_masked (line 14) | def test_caret_not_masked(writer, diagnose):
function test_no_caret_if_no_backtrace (line 28) | def test_no_caret_if_no_backtrace(writer, diagnose):
function test_sink_encoding (line 42) | def test_sink_encoding(writer, encoding):
function test_file_sink_ascii_encoding (line 68) | def test_file_sink_ascii_encoding(tmp_path):
function test_file_sink_utf8_encoding (line 84) | def test_file_sink_utf8_encoding(tmp_path):
function test_has_sys_real_prefix (line 100) | def test_has_sys_real_prefix(writer, monkeypatch):
function test_no_sys_real_prefix (line 113) | def test_no_sys_real_prefix(writer, monkeypatch):
function test_has_site_getsitepackages (line 126) | def test_has_site_getsitepackages(writer, monkeypatch):
function test_no_site_getsitepackages (line 139) | def test_no_site_getsitepackages(writer, monkeypatch):
function test_user_site_is_path (line 152) | def test_user_site_is_path(writer, monkeypatch):
function test_user_site_is_none (line 165) | def test_user_site_is_none(writer, monkeypatch):
function test_sysconfig_get_path_return_path (line 178) | def test_sysconfig_get_path_return_path(writer, monkeypatch):
function test_sysconfig_get_path_return_none (line 191) | def test_sysconfig_get_path_return_none(writer, monkeypatch):
function test_no_exception (line 204) | def test_no_exception(writer):
function test_exception_is_none (line 215) | def test_exception_is_none():
function test_exception_is_tuple (line 229) | def test_exception_is_tuple():
function test_exception_not_raising (line 263) | def test_exception_not_raising(writer, exception):
function test_exception_raising (line 275) | def test_exception_raising(writer, exception):
function test_exclude_exception_raising (line 292) | def test_exclude_exception_raising(writer, exclude, exception):
function test_exclude_exception_not_raising (line 307) | def test_exclude_exception_not_raising(writer, exclude, exception):
function test_reraise (line 318) | def test_reraise(writer):
function test_onerror (line 331) | def test_onerror(writer):
function test_onerror_with_reraise (line 353) | def test_onerror_with_reraise(writer):
function test_decorate_function (line 368) | def test_decorate_function(writer):
function test_decorate_coroutine (line 379) | def test_decorate_coroutine(writer):
function test_decorate_generator (line 392) | def test_decorate_generator(writer):
function test_decorate_generator_with_error (line 407) | def test_decorate_generator_with_error():
function test_default_with_function (line 417) | def test_default_with_function():
function test_default_with_generator (line 425) | def test_default_with_generator():
function test_default_with_coroutine (line 434) | def test_default_with_coroutine():
function test_async_context_manager (line 442) | def test_async_context_manager():
function test_error_when_decorating_class_without_parentheses (line 451) | def test_error_when_decorating_class_without_parentheses():
function test_error_when_decorating_class_with_parentheses (line 459) | def test_error_when_decorating_class_with_parentheses():
function test_unprintable_but_decorated_repr (line 467) | def test_unprintable_but_decorated_repr(writer):
function test_unprintable_but_decorated_repr_without_reraise (line 484) | def test_unprintable_but_decorated_repr_without_reraise(writer):
function test_unprintable_but_decorated_multiple_sinks (line 499) | def test_unprintable_but_decorated_multiple_sinks(capsys):
function test_unprintable_but_decorated_repr_with_enqueue (line 518) | def test_unprintable_but_decorated_repr_with_enqueue(writer):
function test_unprintable_but_decorated_repr_twice (line 538) | def test_unprintable_but_decorated_repr_twice(writer):
function test_unprintable_with_catch_context_manager (line 555) | def test_unprintable_with_catch_context_manager(writer):
function test_unprintable_with_catch_context_manager_reused (line 571) | def test_unprintable_with_catch_context_manager_reused(writer):
function test_unprintable_but_decorated_repr_multiple_threads (line 599) | def test_unprintable_but_decorated_repr_multiple_threads(writer):
FILE: tests/test_exceptions_formatting.py
function normalize (line 14) | def normalize(exception):
function generate (line 89) | def generate(output, outpath):
function compare_exception (line 97) | def compare_exception(dirname, filename):
function test_backtrace (line 156) | def test_backtrace(filename):
function test_diagnose (line 183) | def test_diagnose(filename):
function test_exception_ownership (line 203) | def test_exception_ownership(filename):
function test_exception_others (line 239) | def test_exception_others(filename):
function test_exception_modern (line 266) | def test_exception_modern(filename, minimum_python_version):
function test_group_exception_using_backport (line 279) | def test_group_exception_using_backport(writer):
function test_invalid_format_exception_only_no_output (line 292) | def test_invalid_format_exception_only_no_output(writer, monkeypatch):
function test_invalid_format_exception_only_indented_error_message (line 303) | def test_invalid_format_exception_only_indented_error_message(writer, mo...
function test_invalid_grouped_exception_no_exceptions (line 315) | def test_invalid_grouped_exception_no_exceptions(writer):
FILE: tests/test_filesink_compression.py
function test_compression_ext (line 17) | def test_compression_ext(tmp_path, compression):
function test_compression_function (line 24) | def test_compression_function(tmp_path):
function test_compression_at_rotation (line 35) | def test_compression_at_rotation(tmp_path, mode, freeze_time):
function test_compression_at_remove_without_rotation (line 52) | def test_compression_at_remove_without_rotation(tmp_path, mode):
function test_no_compression_at_remove_with_rotation (line 61) | def test_no_compression_at_remove_with_rotation(tmp_path, mode):
function test_rename_existing_with_creation_time (line 69) | def test_rename_existing_with_creation_time(tmp_path, freeze_time):
function test_renaming_compression_dest_exists (line 85) | def test_renaming_compression_dest_exists(freeze_time, tmp_path):
function test_renaming_compression_dest_exists_with_time (line 103) | def test_renaming_compression_dest_exists_with_time(freeze_time, tmp_path):
function test_compression_use_renamed_file_after_rotation (line 121) | def test_compression_use_renamed_file_after_rotation(tmp_path, freeze_ti...
function test_threaded_compression_after_rotation (line 147) | def test_threaded_compression_after_rotation(tmp_path):
function test_exception_during_compression_at_rotation (line 182) | def test_exception_during_compression_at_rotation(freeze_time, tmp_path,...
function test_exception_during_compression_at_rotation_not_caught (line 212) | def test_exception_during_compression_at_rotation_not_caught(freeze_time...
function test_exception_during_compression_at_remove (line 242) | def test_exception_during_compression_at_remove(tmp_path, capsys, delay):
function test_invalid_compression_type (line 269) | def test_invalid_compression_type(compression):
function test_unknown_compression (line 275) | def test_unknown_compression(compression):
function test_gzip_module_unavailable (line 281) | def test_gzip_module_unavailable(ext, monkeypatch):
function test_bz2_module_unavailable (line 289) | def test_bz2_module_unavailable(ext, monkeypatch):
function test_lzma_module_unavailable (line 297) | def test_lzma_module_unavailable(ext, monkeypatch):
function test_tarfile_module_unavailable (line 305) | def test_tarfile_module_unavailable(ext, monkeypatch):
function test_zipfile_module_unavailable (line 313) | def test_zipfile_module_unavailable(ext, monkeypatch):
FILE: tests/test_filesink_delay.py
function test_file_not_delayed (line 9) | def test_file_not_delayed(tmp_path):
function test_file_delayed (line 17) | def test_file_delayed(tmp_path):
function test_compression (line 25) | def test_compression(tmp_path):
function test_compression_early_remove (line 33) | def test_compression_early_remove(tmp_path):
function test_retention (line 39) | def test_retention(tmp_path):
function test_retention_early_remove (line 50) | def test_retention_early_remove(tmp_path):
function test_rotation (line 60) | def test_rotation(tmp_path, freeze_time):
function test_rotation_early_remove (line 75) | def test_rotation_early_remove(tmp_path):
function test_rotation_and_retention (line 82) | def test_rotation_and_retention(freeze_time, tmp_path):
function test_rotation_and_retention_timed_file (line 101) | def test_rotation_and_retention_timed_file(freeze_time, tmp_path):
FILE: tests/test_filesink_permissions.py
function set_umask (line 10) | def set_umask():
function test_log_file_permissions (line 17) | def test_log_file_permissions(tmp_path, permissions):
function test_rotation_permissions (line 31) | def test_rotation_permissions(tmp_path, permissions, set_umask):
FILE: tests/test_filesink_retention.py
function test_retention_time (line 13) | def test_retention_time(freeze_time, tmp_path, retention):
function test_retention_count (line 31) | def test_retention_count(tmp_path, retention):
function test_retention_function (line 44) | def test_retention_function(tmp_path):
function test_managed_files (line 65) | def test_managed_files(tmp_path):
function test_not_managed_files (line 92) | def test_not_managed_files(tmp_path):
function test_no_duplicates_in_listed_files (line 122) | def test_no_duplicates_in_listed_files(tmp_path, filename):
function test_directories_ignored (line 147) | def test_directories_ignored(tmp_path):
function test_manage_formatted_files (line 159) | def test_manage_formatted_files(freeze_time, tmp_path):
function test_date_with_dot_after_extension (line 185) | def test_date_with_dot_after_extension(tmp_path):
function test_symbol_in_filename (line 195) | def test_symbol_in_filename(tmp_path):
function test_manage_file_without_extension (line 205) | def test_manage_file_without_extension(tmp_path):
function test_manage_formatted_files_without_extension (line 215) | def test_manage_formatted_files_without_extension(tmp_path):
function test_retention_at_rotation (line 228) | def test_retention_at_rotation(tmp_path, mode):
function test_retention_at_remove_without_rotation (line 240) | def test_retention_at_remove_without_rotation(tmp_path, mode):
function test_no_retention_at_remove_with_rotation (line 249) | def test_no_retention_at_remove_with_rotation(tmp_path, mode):
function test_no_renaming (line 257) | def test_no_renaming(tmp_path):
function test_exception_during_retention_at_rotation (line 266) | def test_exception_during_retention_at_rotation(freeze_time, tmp_path, c...
function test_exception_during_retention_at_rotation_not_caught (line 296) | def test_exception_during_retention_at_rotation_not_caught(freeze_time, ...
function test_exception_during_retention_at_remove (line 325) | def test_exception_during_retention_at_remove(tmp_path, capsys, delay):
function test_invalid_retention_type (line 347) | def test_invalid_retention_type(retention):
function test_unparsable_retention (line 355) | def test_unparsable_retention(retention):
function test_invalid_value_retention_duration (line 361) | def test_invalid_value_retention_duration(retention):
FILE: tests/test_filesink_rotation.py
function tmp_path_local (line 18) | def tmp_path_local(reset_logger):
function test_renaming (line 26) | def test_renaming(freeze_time, tmp_path):
function test_no_renaming (line 54) | def test_no_renaming(freeze_time, tmp_path):
function test_size_rotation (line 81) | def test_size_rotation(freeze_time, tmp_path, size):
function test_time_rotation (line 148) | def test_time_rotation(freeze_time, tmp_path, when, hours):
function test_time_rotation_dst (line 167) | def test_time_rotation_dst(freeze_time, tmp_path):
function test_time_rotation_with_tzinfo_diff_bigger (line 190) | def test_time_rotation_with_tzinfo_diff_bigger(freeze_time, tmp_path):
function test_time_rotation_with_tzinfo_diff_lower (line 213) | def test_time_rotation_with_tzinfo_diff_lower(freeze_time, tmp_path):
function test_time_rotation_with_tzinfo_utc (line 236) | def test_time_rotation_with_tzinfo_utc(freeze_time, tmp_path):
function test_time_rotation_multiple_days_at_midnight_utc (line 262) | def test_time_rotation_multiple_days_at_midnight_utc(freeze_time, tmp_pa...
function test_daily_rotation_with_different_timezone (line 294) | def test_daily_rotation_with_different_timezone(freeze_time, tmp_path, o...
function test_time_rotation_after_positive_timezone_changes_forward (line 332) | def test_time_rotation_after_positive_timezone_changes_forward(freeze_ti...
function test_time_rotation_when_positive_timezone_changes_forward (line 359) | def test_time_rotation_when_positive_timezone_changes_forward(freeze_tim...
function test_time_rotation_after_negative_timezone_changes_forward (line 395) | def test_time_rotation_after_negative_timezone_changes_forward(freeze_ti...
function test_time_rotation_when_negative_timezone_changes_forward (line 422) | def test_time_rotation_when_negative_timezone_changes_forward(freeze_tim...
function test_time_rotation_after_positive_timezone_changes_backward_aware (line 456) | def test_time_rotation_after_positive_timezone_changes_backward_aware(
function test_time_rotation_after_positive_timezone_changes_backward_naive (line 485) | def test_time_rotation_after_positive_timezone_changes_backward_naive(
function test_time_rotation_after_negative_timezone_changes_backward_aware (line 523) | def test_time_rotation_after_negative_timezone_changes_backward_aware(
function test_time_rotation_after_negative_timezone_changes_backward_naive (line 552) | def test_time_rotation_after_negative_timezone_changes_backward_naive(
function test_time_rotation_when_timezone_changes_backward_rename_file (line 582) | def test_time_rotation_when_timezone_changes_backward_rename_file(freeze...
function test_dont_rotate_earlier_when_utc_is_one_day_before (line 618) | def test_dont_rotate_earlier_when_utc_is_one_day_before(freeze_time, tmp...
function test_dont_rotate_later_when_utc_is_one_day_after (line 653) | def test_dont_rotate_later_when_utc_is_one_day_after(freeze_time, tmp_pa...
function test_rotation_at_midnight_with_date_in_filename (line 679) | def test_rotation_at_midnight_with_date_in_filename(freeze_time, tmp_pat...
function test_time_rotation_reopening_native (line 701) | def test_time_rotation_reopening_native(tmp_path_local, delay):
function test_time_rotation_reopening_xattr_attributeerror (line 757) | def test_time_rotation_reopening_xattr_attributeerror(tmp_path_local, mo...
function test_time_rotation_reopening_xattr_oserror (line 793) | def test_time_rotation_reopening_xattr_oserror(tmp_path_local, monkeypat...
function test_time_rotation_windows_no_setctime (line 822) | def test_time_rotation_windows_no_setctime(tmp_path, monkeypatch):
function test_time_rotation_windows_setctime_exception (line 846) | def test_time_rotation_windows_setctime_exception(tmp_path, monkeypatch,...
function test_function_rotation (line 867) | def test_function_rotation(freeze_time, tmp_path):
function test_rotation_at_remove (line 899) | def test_rotation_at_remove(freeze_time, tmp_path, mode):
function test_no_rotation_at_remove (line 914) | def test_no_rotation_at_remove(tmp_path, mode):
function test_rename_existing_with_creation_time (line 922) | def test_rename_existing_with_creation_time(freeze_time, tmp_path):
function test_renaming_rotation_dest_exists (line 938) | def test_renaming_rotation_dest_exists(freeze_time, tmp_path):
function test_renaming_rotation_dest_exists_with_time (line 956) | def test_renaming_rotation_dest_exists_with_time(freeze_time, tmp_path):
function test_exception_during_rotation (line 976) | def test_exception_during_rotation(tmp_path, capsys):
function test_exception_during_rotation_not_caught (line 995) | def test_exception_during_rotation_not_caught(tmp_path, capsys):
function test_recipe_rotation_both_size_and_time (line 1014) | def test_recipe_rotation_both_size_and_time(freeze_time, tmp_path):
function test_multiple_rotation_conditions (line 1061) | def test_multiple_rotation_conditions(freeze_time, tmp_path):
function test_empty_rotation_condition_list (line 1086) | def test_empty_rotation_condition_list():
function test_invalid_rotation_type (line 1094) | def test_invalid_rotation_type(rotation):
function test_unparsable_rotation (line 1115) | def test_unparsable_rotation(rotation):
function test_invalid_day_rotation (line 1121) | def test_invalid_day_rotation(rotation):
function test_invalid_time_rotation (line 1129) | def test_invalid_time_rotation(rotation):
function test_invalid_value_size_rotation (line 1135) | def test_invalid_value_size_rotation(rotation):
function test_invalid_unit_rotation_duration (line 1141) | def test_invalid_unit_rotation_duration(rotation):
function test_invalid_value_rotation_duration (line 1147) | def test_invalid_value_rotation_duration(rotation):
FILE: tests/test_filesink_watch.py
function test_file_deleted_before_write_without_delay (line 12) | def test_file_deleted_before_write_without_delay(tmp_path):
function test_file_deleted_before_write_with_delay (line 21) | def test_file_deleted_before_write_with_delay(tmp_path):
function test_file_path_containing_placeholder (line 31) | def test_file_path_containing_placeholder(tmp_path):
function test_file_reopened_with_arguments (line 42) | def test_file_reopened_with_arguments(tmp_path):
function test_file_manually_changed (line 51) | def test_file_manually_changed(tmp_path):
function test_file_folder_deleted (line 61) | def test_file_folder_deleted(tmp_path):
function test_file_deleted_before_rotation (line 71) | def test_file_deleted_before_rotation(tmp_path):
function test_file_deleted_before_compression (line 87) | def test_file_deleted_before_compression(tmp_path):
function test_file_deleted_before_retention (line 103) | def test_file_deleted_before_retention(tmp_path):
function test_file_correctly_reused_after_rotation (line 118) | def test_file_correctly_reused_after_rotation(tmp_path):
function test_file_closed_without_being_logged (line 138) | def test_file_closed_without_being_logged(tmp_path, delay, compression):
FILE: tests/test_formatting.py
function test_log_formatters (line 37) | def test_log_formatters(format, validator, writer, use_log_function):
function test_file_formatters (line 59) | def test_file_formatters(tmp_path, format, validator, part):
function test_log_formatting (line 105) | def test_log_formatting(writer, message, args, kwargs, expected, use_log...
function test_formatting_missing_lineno_frame_context (line 116) | def test_formatting_missing_lineno_frame_context(writer, missing_frame_l...
function test_formatting_incomplete_frame_context (line 122) | def test_formatting_incomplete_frame_context(writer, incomplete_frame_co...
function test_extra_formatting (line 128) | def test_extra_formatting(writer):
function test_kwargs_in_extra_dict (line 135) | def test_kwargs_in_extra_dict():
function test_non_string_message (line 176) | def test_non_string_message(writer):
function test_non_string_message_is_str_in_record (line 187) | def test_non_string_message_is_str_in_record(writer, colors):
function test_missing_positional_field_during_formatting (line 205) | def test_missing_positional_field_during_formatting(writer, colors):
function test_missing_named_field_during_formatting (line 215) | def test_missing_named_field_during_formatting(writer, colors):
function test_malformed_curly_braces_during_formatting (line 225) | def test_malformed_curly_braces_during_formatting(writer, colors):
function test_not_formattable_message (line 235) | def test_not_formattable_message(writer, colors):
function test_invalid_color_markup (line 244) | def test_invalid_color_markup(writer):
FILE: tests/test_get_frame.py
function test_with_sys_getframe (line 7) | def test_with_sys_getframe(monkeypatch):
function test_without_sys_getframe (line 16) | def test_without_sys_getframe(monkeypatch):
function test_get_frame_fallback (line 22) | def test_get_frame_fallback():
FILE: tests/test_interception.py
class InterceptHandler (line 9) | class InterceptHandler(logging.Handler):
method emit (line 10) | def emit(self, record):
function test_formatting (line 31) | def test_formatting(writer):
function test_intercept (line 50) | def test_intercept(writer):
function test_add_before_intercept (line 60) | def test_add_before_intercept(writer):
function test_remove_interception (line 70) | def test_remove_interception(writer):
function test_intercept_too_low (line 83) | def test_intercept_too_low(writer):
function test_multiple_intercept (line 93) | def test_multiple_intercept(writer):
function test_exception (line 104) | def test_exception(writer):
function test_level_is_no (line 119) | def test_level_is_no(writer):
function test_level_does_not_exist (line 128) | def test_level_does_not_exist(writer):
function test_level_exist_builtin (line 139) | def test_level_exist_builtin(writer):
function test_level_exists_custom (line 148) | def test_level_exists_custom(writer):
function test_using_logging_function (line 160) | def test_using_logging_function(writer):
FILE: tests/test_levels.py
function test_log_int_level (line 10) | def test_log_int_level(writer):
function test_log_str_level (line 17) | def test_log_str_level(writer):
function test_add_level (line 24) | def test_add_level(writer):
function test_add_level_after_add (line 41) | def test_add_level_after_add(writer, colorize, expected):
function test_add_level_then_log_with_int_value (line 49) | def test_add_level_then_log_with_int_value(writer):
function test_add_malicious_level (line 58) | def test_add_malicious_level(writer):
function test_add_existing_level (line 71) | def test_add_existing_level(writer):
function test_blank_color (line 89) | def test_blank_color(writer):
function test_edit_level (line 96) | def test_edit_level(writer):
function test_edit_existing_level (line 116) | def test_edit_existing_level(writer):
function test_get_level (line 124) | def test_get_level():
function test_get_existing_level (line 130) | def test_get_existing_level():
function test_add_custom_level (line 134) | def test_add_custom_level(writer):
function test_updating_min_level (line 149) | def test_updating_min_level(writer):
function test_assign_custom_level_method (line 165) | def test_assign_custom_level_method(writer):
function test_updating_level_no_not_allowed_default (line 183) | def test_updating_level_no_not_allowed_default():
function test_updating_level_no_not_allowed_custom (line 190) | def test_updating_level_no_not_allowed_custom():
function test_log_invalid_level_type (line 199) | def test_log_invalid_level_type(writer, level):
function test_log_invalid_level_value (line 208) | def test_log_invalid_level_value(writer, level):
function test_log_unknown_level (line 217) | def test_log_unknown_level(writer, level):
function test_add_invalid_level_name (line 224) | def test_add_invalid_level_name(level_name):
function test_add_invalid_level_type (line 230) | def test_add_invalid_level_type(level_value):
function test_add_invalid_level_value (line 236) | def test_add_invalid_level_value(level_value):
function test_get_invalid_level (line 244) | def test_get_invalid_level(level):
function test_get_unknown_level (line 249) | def test_get_unknown_level():
function test_edit_invalid_level (line 255) | def test_edit_invalid_level(level):
function test_edit_unknown_level (line 261) | def test_edit_unknown_level(level_name):
function test_add_level_unknown_color (line 270) | def test_add_level_unknown_color(color):
function test_add_level_invalid_markup (line 282) | def test_add_level_invalid_markup(color):
function test_add_level_invalid_name (line 290) | def test_add_level_invalid_name(color):
FILE: tests/test_locks.py
class CyclicReference (line 12) | class CyclicReference:
method __init__ (line 24) | def __init__(self, _other: "CyclicReference" = None):
method __del__ (line 27) | def __del__(self):
function _remove_cyclic_references (line 32) | def _remove_cyclic_references():
function test_no_deadlock_on_generational_garbage_collection (line 41) | def test_no_deadlock_on_generational_garbage_collection():
function test_no_deadlock_if_logger_used_inside_sink_with_catch (line 71) | def test_no_deadlock_if_logger_used_inside_sink_with_catch(capsys):
function test_no_deadlock_if_logger_used_inside_sink_without_catch (line 84) | def test_no_deadlock_if_logger_used_inside_sink_without_catch():
function test_no_error_if_multithreading (line 94) | def test_no_error_if_multithreading(capsys):
function _pickle_sink (line 118) | def _pickle_sink(message):
function test_pickled_logger_does_not_inherit_acquired_local (line 127) | def test_pickled_logger_does_not_inherit_acquired_local(capsys):
FILE: tests/test_multiprocessing.py
function fork_context (line 17) | def fork_context():
function spawn_context (line 22) | def spawn_context():
function do_something (line 26) | def do_something(i):
function set_logger (line 30) | def set_logger(logger_):
function subworker (line 35) | def subworker(logger_):
function subworker_inheritance (line 39) | def subworker_inheritance():
function subworker_remove (line 43) | def subworker_remove(logger_):
function subworker_remove_inheritance (line 49) | def subworker_remove_inheritance():
function subworker_complete (line 55) | def subworker_complete(logger_):
function subworker_complete_inheritance (line 64) | def subworker_complete_inheritance():
function subworker_barrier (line 73) | def subworker_barrier(logger_, barrier):
function subworker_barrier_inheritance (line 80) | def subworker_barrier_inheritance(barrier):
class Writer (line 87) | class Writer:
method __init__ (line 88) | def __init__(self):
method write (line 91) | def write(self, message):
method read (line 94) | def read(self):
function test_apply_spawn (line 98) | def test_apply_spawn(spawn_context):
function test_apply_fork (line 116) | def test_apply_fork(fork_context):
function test_apply_inheritance (line 134) | def test_apply_inheritance(fork_context):
function test_apply_async_spawn (line 151) | def test_apply_async_spawn(spawn_context):
function test_apply_async_fork (line 170) | def test_apply_async_fork(fork_context):
function test_apply_async_inheritance (line 189) | def test_apply_async_inheritance(fork_context):
function test_process_spawn (line 207) | def test_process_spawn(spawn_context):
function test_process_fork (line 225) | def test_process_fork(fork_context):
function test_process_inheritance (line 243) | def test_process_inheritance(fork_context):
function test_remove_in_child_process_spawn (line 260) | def test_remove_in_child_process_spawn(spawn_context):
function test_remove_in_child_process_fork (line 278) | def test_remove_in_child_process_fork(fork_context):
function test_remove_in_child_process_inheritance (line 296) | def test_remove_in_child_process_inheritance(fork_context):
function test_remove_in_main_process_spawn (line 313) | def test_remove_in_main_process_spawn(spawn_context):
function test_remove_in_main_process_fork (line 335) | def test_remove_in_main_process_fork(fork_context):
function test_remove_in_main_process_inheritance (line 354) | def test_remove_in_main_process_inheritance(fork_context):
function test_await_complete_spawn (line 372) | def test_await_complete_spawn(capsys, spawn_context):
function test_await_complete_fork (line 398) | def test_await_complete_fork(capsys, fork_context):
function test_await_complete_inheritance (line 424) | def test_await_complete_inheritance(capsys, fork_context):
function test_not_picklable_sinks_spawn (line 449) | def test_not_picklable_sinks_spawn(spawn_context, tmp_path, capsys):
function test_not_picklable_sinks_fork (line 476) | def test_not_picklable_sinks_fork(capsys, tmp_path, fork_context):
function test_not_picklable_sinks_inheritance (line 509) | def test_not_picklable_sinks_inheritance(capsys, tmp_path, fork_context):
function test_no_deadlock_if_internal_lock_in_use (line 545) | def test_no_deadlock_if_internal_lock_in_use(tmp_path, enqueue, deepcopi...
function test_no_deadlock_if_external_lock_in_use (line 589) | def test_no_deadlock_if_external_lock_in_use(enqueue, capsys, fork_conte...
function test_complete_from_multiple_child_processes (line 610) | def test_complete_from_multiple_child_processes(capsys, fork_context):
FILE: tests/test_opt.py
function test_record (line 11) | def test_record(writer):
function test_record_in_kwargs_too (line 21) | def test_record_in_kwargs_too(writer):
function test_record_not_in_extra (line 34) | def test_record_not_in_extra():
function test_kwargs_in_extra_of_record (line 48) | def test_kwargs_in_extra_of_record():
function test_exception_boolean (line 63) | def test_exception_boolean(writer):
function test_exception_exc_info (line 77) | def test_exception_exc_info(writer):
function test_exception_class (line 93) | def test_exception_class(writer):
function test_exception_log_function (line 109) | def test_exception_log_function(writer):
function test_lazy (line 123) | def test_lazy(writer):
function test_lazy_function_executed_only_once (line 156) | def test_lazy_function_executed_only_once(writer):
function test_logging_within_lazy_function (line 172) | def test_logging_within_lazy_function(writer):
function test_depth (line 188) | def test_depth(writer):
function test_depth_with_unreachable_frame (line 203) | def test_depth_with_unreachable_frame(writer):
function test_capture (line 210) | def test_capture(writer):
function test_colors (line 218) | def test_colors(writer):
function test_colors_not_colorize (line 228) | def test_colors_not_colorize(writer):
function test_colors_doesnt_color_unrelated (line 234) | def test_colors_doesnt_color_unrelated(writer):
function test_colors_doesnt_strip_unrelated (line 240) | def test_colors_doesnt_strip_unrelated(writer):
function test_colors_doesnt_raise_unrelated_colorize (line 246) | def test_colors_doesnt_raise_unrelated_colorize(writer):
function test_colors_doesnt_raise_unrelated_not_colorize (line 252) | def test_colors_doesnt_raise_unrelated_not_colorize(writer):
function test_colors_doesnt_raise_unrelated_colorize_dynamic (line 258) | def test_colors_doesnt_raise_unrelated_colorize_dynamic(writer):
function test_colors_doesnt_raise_unrelated_not_colorize_dynamic (line 264) | def test_colors_doesnt_raise_unrelated_not_colorize_dynamic(writer):
function test_colors_within_record (line 271) | def test_colors_within_record(writer, colorize):
function test_colors_nested (line 279) | def test_colors_nested(writer, colorize):
function test_colors_stripped_in_message_record (line 288) | def test_colors_stripped_in_message_record(colorize):
function test_invalid_markup_in_message (line 302) | def test_invalid_markup_in_message(writer, message, colorize):
function test_colors_with_args (line 312) | def test_colors_with_args(writer, colorize):
function test_colors_with_level (line 319) | def test_colors_with_level(writer, colorize):
function test_colors_double_message (line 327) | def test_colors_double_message(writer, colorize):
function test_colors_multiple_calls (line 340) | def test_colors_multiple_calls(writer, colorize):
function test_colors_multiple_calls_level_color_changed (line 348) | def test_colors_multiple_calls_level_color_changed(writer, colorize):
function test_colors_with_dynamic_formatter (line 358) | def test_colors_with_dynamic_formatter(writer, colorize):
function test_colors_with_format_specs (line 365) | def test_colors_with_format_specs(writer, colorize):
function test_colors_with_message_specs (line 373) | def test_colors_with_message_specs(writer, colorize):
function test_colored_string_used_as_spec (line 383) | def test_colored_string_used_as_spec(writer, colorize):
function test_colored_string_getitem (line 390) | def test_colored_string_getitem(writer, colorize):
function test_colors_without_formatting_args (line 397) | def test_colors_without_formatting_args(writer, colorize):
function test_colors_with_recursion_depth_exceeded_in_format (line 405) | def test_colors_with_recursion_depth_exceeded_in_format(writer, colorize):
function test_colors_with_recursion_depth_exceeded_in_message (line 413) | def test_colors_with_recursion_depth_exceeded_in_message(writer, colorize):
function test_colors_with_auto_indexing (line 421) | def test_colors_with_auto_indexing(writer, colorize):
function test_colors_with_manual_indexing (line 428) | def test_colors_with_manual_indexing(writer, colorize):
function test_colors_with_invalid_indexing (line 436) | def test_colors_with_invalid_indexing(writer, colorize, message):
function test_raw (line 446) | def test_raw(writer):
function test_raw_with_format_function (line 453) | def test_raw_with_format_function(writer):
function test_raw_with_colors (line 460) | def test_raw_with_colors(writer, colorize):
function test_args_with_colors_not_formatted_twice (line 466) | def test_args_with_colors_not_formatted_twice(capsys):
function test_level_tag_wrapping_with_colors (line 481) | def test_level_tag_wrapping_with_colors(writer, colorize):
function test_all_colors_combinations (line 499) | def test_all_colors_combinations(writer, dynamic_format, colorize, color...
function test_raw_with_record (line 560) | def test_raw_with_record(writer):
function test_keep_extra (line 566) | def test_keep_extra(writer):
function test_before_bind (line 575) | def test_before_bind(writer):
function test_deprecated_ansi_argument (line 581) | def test_deprecated_ansi_argument(writer):
function test_message_update_not_overridden_by_patch (line 589) | def test_message_update_not_overridden_by_patch(writer, colors):
function test_message_update_not_overridden_by_format (line 600) | def test_message_update_not_overridden_by_format(writer, colors):
function test_message_update_not_overridden_by_filter (line 612) | def test_message_update_not_overridden_by_filter(writer, colors):
function test_message_update_not_overridden_by_raw (line 624) | def test_message_update_not_overridden_by_raw(writer, colors):
function test_overridden_message_ignore_colors (line 630) | def test_overridden_message_ignore_colors(writer):
FILE: tests/test_parse.py
function fileobj (line 14) | def fileobj():
function test_parse_file (line 19) | def test_parse_file(tmp_path):
function test_parse_fileobj (line 26) | def test_parse_fileobj(tmp_path):
function test_parse_pathlib (line 34) | def test_parse_pathlib(tmp_path):
function test_parse_string_pattern (line 41) | def test_parse_string_pattern(fileobj):
function test_parse_regex_pattern (line 46) | def test_parse_regex_pattern(fileobj):
function test_parse_multiline_pattern (line 52) | def test_parse_multiline_pattern(fileobj):
function test_parse_without_group (line 57) | def test_parse_without_group(fileobj):
function test_parse_bytes (line 62) | def test_parse_bytes():
function test_chunk (line 69) | def test_chunk(fileobj, chunk):
function test_positive_lookbehind_pattern (line 74) | def test_positive_lookbehind_pattern():
function test_greedy_pattern (line 82) | def test_greedy_pattern():
function test_cast_dict (line 90) | def test_cast_dict(tmp_path):
function test_cast_function (line 99) | def test_cast_function(tmp_path):
function test_cast_with_irrelevant_arg (line 113) | def test_cast_with_irrelevant_arg(tmp_path):
function test_cast_with_irrelevant_value (line 122) | def test_cast_with_irrelevant_value(tmp_path):
function test_invalid_file (line 132) | def test_invalid_file(file):
function test_invalid_pattern (line 138) | def test_invalid_pattern(fileobj, pattern):
function test_invalid_cast (line 144) | def test_invalid_cast(fileobj, cast):
FILE: tests/test_patch.py
function test_patch_after_add (line 4) | def test_patch_after_add(writer):
function test_patch_before_add (line 12) | def test_patch_before_add(writer):
function test_add_using_patched (line 20) | def test_add_using_patched(writer):
function test_not_override_parent_logger (line 30) | def test_not_override_parent_logger(writer):
function test_override_previous_patched (line 41) | def test_override_previous_patched(writer):
function test_no_conflict (line 48) | def test_no_conflict(writer):
function test_override_configured (line 61) | def test_override_configured(writer):
function test_multiple_patches (line 72) | def test_multiple_patches(writer):
FILE: tests/test_pickling.py
function print_ (line 14) | def print_(message):
function async_print (line 18) | async def async_print(msg):
function copied_logger_though_pickle (line 23) | def copied_logger_though_pickle(logger):
class StreamHandler (line 32) | class StreamHandler:
method __init__ (line 33) | def __init__(self, flushable=False, stoppable=False):
method write (line 43) | def write(self, message):
method _flush (line 46) | def _flush(self):
method _stop (line 49) | def _stop(self):
class MockLock (line 53) | class MockLock:
method __enter__ (line 54) | def __enter__(self):
method __exit__ (line 57) | def __exit__(self, *excinfo):
class StandardHandler (line 61) | class StandardHandler(logging.Handler):
method __init__ (line 62) | def __init__(self, level):
method emit (line 66) | def emit(self, record):
method acquire (line 69) | def acquire(self):
method release (line 72) | def release(self):
method createLock (line 75) | def createLock(self): # noqa: N802
function format_function (line 79) | def format_function(record):
function filter_function (line 83) | def filter_function(record):
function patch_function (line 87) | def patch_function(record):
function rotation_function (line 91) | def rotation_function(message, file):
function retention_function (line 95) | def retention_function(files):
function compression_function (line 99) | def compression_function(path):
function test_pickling_function_handler (line 103) | def test_pickling_function_handler(capsys):
function test_pickling_coroutine_function_handler (line 112) | def test_pickling_coroutine_function_handler(capsys):
function test_pickling_stream_handler (line 130) | def test_pickling_stream_handler(flushable, stoppable):
function test_pickling_standard_handler (line 141) | def test_pickling_standard_handler():
function test_pickling_standard_handler_root_logger_not_picklable (line 150) | def test_pickling_standard_handler_root_logger_not_picklable(monkeypatch...
function test_pickling_file_handler (line 169) | def test_pickling_file_handler(tmp_path):
function test_pickling_file_handler_rotation (line 190) | def test_pickling_file_handler_rotation(tmp_path, rotation):
function test_pickling_file_handler_retention (line 201) | def test_pickling_file_handler_retention(tmp_path, retention):
function test_pickling_file_handler_compression (line 210) | def test_pickling_file_handler_compression(tmp_path, compression):
function test_pickling_no_handler (line 218) | def test_pickling_no_handler(writer):
function test_pickling_handler_not_serializable (line 225) | def test_pickling_handler_not_serializable():
function test_pickling_filter_function (line 231) | def test_pickling_filter_function(capsys):
function test_pickling_filter_name (line 242) | def test_pickling_filter_name(capsys, filter):
function test_pickling_format_string (line 252) | def test_pickling_format_string(capsys, colorize):
function test_pickling_format_function (line 262) | def test_pickling_format_function(capsys, colorize):
function test_pickling_filter_function_not_serializable (line 271) | def test_pickling_filter_function_not_serializable():
function test_pickling_format_function_not_serializable (line 277) | def test_pickling_format_function_not_serializable():
function test_pickling_bound_logger (line 283) | def test_pickling_bound_logger(writer):
function test_pickling_patched_logger (line 291) | def test_pickling_patched_logger(writer):
function test_remove_after_pickling (line 299) | def test_remove_after_pickling(capsys):
function test_pickling_logging_method (line 310) | def test_pickling_logging_method(capsys):
function test_pickling_log_method (line 320) | def test_pickling_log_method(capsys):
function test_pickling_no_error (line 347) | def test_pickling_no_error(method):
FILE: tests/test_propagation.py
class PropagateHandler (line 12) | class PropagateHandler(logging.Handler):
method emit (line 13) | def emit(self, record):
function test_formatting (line 17) | def test_formatting(capsys):
function test_propagate (line 37) | def test_propagate(capsys):
function test_remove_propagation (line 52) | def test_remove_propagation(capsys):
function test_propagate_too_high (line 69) | def test_propagate_too_high(capsys):
function test_exception (line 83) | def test_exception(capsys, use_opt):
FILE: tests/test_recattr.py
function test_patch_record_file (line 7) | def test_patch_record_file(writer):
function test_patch_record_thread (line 18) | def test_patch_record_thread(writer):
function test_patch_record_process (line 29) | def test_patch_record_process(writer):
function test_patch_record_exception (line 40) | def test_patch_record_exception(writer):
function test_level_repr (line 54) | def test_level_repr():
function test_file_repr (line 59) | def test_file_repr():
function test_thread_repr (line 64) | def test_thread_repr():
function test_process_repr (line 69) | def test_process_repr():
function test_exception_repr (line 74) | def test_exception_repr():
FILE: tests/test_reinstall.py
function fork_context (line 10) | def fork_context():
function spawn_context (line 15) | def spawn_context():
class Writer (line 19) | class Writer:
method __init__ (line 20) | def __init__(self):
method write (line 23) | def write(self, message):
method read (line 26) | def read(self):
function subworker (line 30) | def subworker(logger):
function poolworker (line 36) | def poolworker(_):
function deeper_subworker (line 41) | def deeper_subworker():
function test_process_fork (line 46) | def test_process_fork(fork_context):
function test_process_spawn (line 63) | def test_process_spawn(spawn_context):
function test_pool_fork (line 81) | def test_pool_fork(fork_context):
function test_pool_spawn (line 95) | def test_pool_spawn(spawn_context):
FILE: tests/test_remove.py
class StopSinkError (line 9) | class StopSinkError:
method write (line 10) | def write(self, message):
method stop (line 13) | def stop(self):
function test_remove_all (line 17) | def test_remove_all(tmp_path, writer, capsys):
function test_remove_simple (line 44) | def test_remove_simple(writer):
function test_remove_enqueue (line 52) | def test_remove_enqueue(writer):
function test_remove_enqueue_filesink (line 61) | def test_remove_enqueue_filesink(tmp_path):
function test_exception_in_stop_during_remove_one (line 69) | def test_exception_in_stop_during_remove_one(capsys):
function test_exception_in_stop_not_caught_during_remove_all (line 82) | def test_exception_in_stop_not_caught_during_remove_all(capsys):
function test_invalid_handler_id_value (line 102) | def test_invalid_handler_id_value(writer):
function test_invalid_handler_id_type (line 110) | def test_invalid_handler_id_type(handler_id):
FILE: tests/test_repr.py
function test_no_handler (line 9) | def test_no_handler():
function test_stderr (line 13) | def test_stderr():
function test_stdout (line 18) | def test_stdout():
function test_file_object (line 23) | def test_file_object(tmp_path):
function test_file_str (line 30) | def test_file_str(tmp_path):
function test_file_pathlib (line 36) | def test_file_pathlib(tmp_path):
function test_stream_object (line 42) | def test_stream_object():
function test_stream_object_without_name_attr (line 57) | def test_stream_object_without_name_attr():
function test_stream_object_with_empty_name (line 69) | def test_stream_object_with_empty_name():
function test_function (line 84) | def test_function():
function test_callable_without_name (line 92) | def test_callable_without_name():
function test_callable_with_empty_name (line 104) | def test_callable_with_empty_name():
function test_coroutine_function (line 118) | def test_coroutine_function():
function test_coroutine_callable_without_name (line 126) | def test_coroutine_callable_without_name():
function test_coroutine_function_with_empty_name (line 140) | def test_coroutine_function_with_empty_name():
function test_standard_handler (line 154) | def test_standard_handler():
function test_multiple_handlers (line 165) | def test_multiple_handlers():
function test_handler_removed (line 177) | def test_handler_removed():
function test_handler_level_name (line 184) | def test_handler_level_name():
function test_handler_level_num (line 189) | def test_handler_level_num():
FILE: tests/test_standard_handler.py
class RejectAllFilter (line 9) | class RejectAllFilter(Filter):
method filter (line 10) | def filter(self, record):
function test_stream_handler (line 14) | def test_stream_handler(capsys):
function test_file_handler (line 25) | def test_file_handler(tmp_path):
function test_null_handler (line 35) | def test_null_handler(capsys):
function test_extra_dict (line 45) | def test_extra_dict(capsys):
function test_no_conflict_with_extra_dict (line 56) | def test_no_conflict_with_extra_dict(capsys):
function test_no_exception (line 65) | def test_no_exception():
function test_exception (line 83) | def test_exception(capsys):
function test_exception_formatting (line 101) | def test_exception_formatting(tmp_path):
function test_standard_formatter (line 121) | def test_standard_formatter(capsys, dynamic_format):
function test_standard_formatter_with_new_line (line 139) | def test_standard_formatter_with_new_line(capsys, dynamic_format):
function test_raw_standard_formatter (line 157) | def test_raw_standard_formatter(capsys, dynamic_format):
function test_raw_standard_formatter_with_new_line (line 175) | def test_raw_standard_formatter_with_new_line(capsys, dynamic_format):
function test_standard_formatter_with_non_standard_level_name (line 192) | def test_standard_formatter_with_non_standard_level_name(capsys):
function test_standard_formatter_with_custom_level_name (line 203) | def test_standard_formatter_with_custom_level_name(capsys):
function test_standard_formatter_with_unregistered_level (line 215) | def test_standard_formatter_with_unregistered_level(capsys):
function test_standard_handler_with_configured_filter (line 226) | def test_standard_handler_with_configured_filter(capsys):
function test_standard_handler_with_configured_level (line 240) | def test_standard_handler_with_configured_level(capsys):
FILE: tests/test_threading.py
class NonSafeSink (line 8) | class NonSafeSink:
method __init__ (line 9) | def __init__(self, sleep_time, stop_time=0):
method write (line 15) | def write(self, message):
method stop (line 24) | def stop(self):
function test_safe_logging (line 29) | def test_safe_logging():
function test_safe_adding_while_logging (line 53) | def test_safe_adding_while_logging(writer):
function test_safe_removing_while_logging (line 85) | def test_safe_removing_while_logging(capsys):
function test_safe_removing_all_while_logging (line 116) | def test_safe_removing_all_while_logging(capsys):
function test_safe_slow_removing_all_while_logging (line 144) | def test_safe_slow_removing_all_while_logging(capsys):
function test_safe_writing_after_removing (line 173) | def test_safe_writing_after_removing(capsys):
function test_heavily_threaded_logging (line 203) | def test_heavily_threaded_logging(capsys):
FILE: tests/test_type_hinting.py
function test_mypy_import (line 6) | def test_mypy_import():
Condensed preview — 305 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,136K chars).
[
{
"path": ".codecov.yml",
"chars": 134,
"preview": "comment: false\n\ncoverage:\n status:\n patch: no\n changes: no\n project:\n default:\n target: 100%\n "
},
{
"path": ".github/SECURITY.md",
"chars": 761,
"preview": "# Security Policy\n\n## Supported Versions\n\nAs an open source product, only the latest major version will be patched for "
},
{
"path": ".github/dependabot.yml",
"chars": 419,
"preview": "version: 2\nupdates:\n- package-ecosystem: github-actions\n directory: /\n schedule:\n interval: quarterly\n labels: []\n"
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 745,
"preview": "name: CodeQL\n\non:\n push:\n pull_request:\n schedule:\n - cron: 0 0 * * 0\n\njobs:\n analyze:\n if: github.event_name !="
},
{
"path": ".github/workflows/docs.yml",
"chars": 561,
"preview": "name: Docs\n\non: [push, pull_request]\n\njobs:\n docs:\n if: github.event_name != 'pull_request' || github.event.pull_req"
},
{
"path": ".github/workflows/lint.yml",
"chars": 553,
"preview": "name: Lint\n\non: [push, pull_request]\n\njobs:\n lint:\n if: github.event_name != 'pull_request' || github.event.pull_req"
},
{
"path": ".github/workflows/packaging.yml",
"chars": 1154,
"preview": "name: Packaging\n\non: [push, pull_request]\n\njobs:\n build:\n if: github.event_name != 'pull_request' || github.event.pu"
},
{
"path": ".github/workflows/tests.yml",
"chars": 2332,
"preview": "name: Tests\n\non:\n push:\n pull_request:\n schedule:\n - cron: 0 0 * * 0\n\njobs:\n tests:\n if: github.event_name != 'p"
},
{
"path": ".gitignore",
"chars": 1176,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": ".pre-commit-config.yaml",
"chars": 848,
"preview": "repos:\n- repo: https://github.com/pre-commit/pre-commit-hooks\n rev: v6.0.0\n hooks:\n - id: end-of-file-fixer\n - id: f"
},
{
"path": ".readthedocs.yml",
"chars": 236,
"preview": "version: 2\n\nbuild:\n os: ubuntu-20.04\n tools:\n python: '3.11'\n\npython:\n install:\n - method: pip\n path: .\n ex"
},
{
"path": ".vscode/settings.json",
"chars": 236,
"preview": "{\n \"python.testing.pytestArgs\": [\n \"tests\"\n ],\n \"python.testing.unittestEnabled\": false,\n \"python.tes"
},
{
"path": "CHANGELOG.rst",
"chars": 25544,
"preview": "`Unreleased`_\n=============\n\n- Add new ``logger.reinstall()`` method to automatically set up the ``logger`` in spawned c"
},
{
"path": "CONTRIBUTING.rst",
"chars": 3968,
"preview": "Thank you for considering improving `Loguru`, any contribution is much welcome!\n\n.. _minimal reproducible example: https"
},
{
"path": "LICENSE",
"chars": 1056,
"preview": "MIT License\n\nCopyright (c) 2017\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this so"
},
{
"path": "README.md",
"chars": 19474,
"preview": "<p align=\"center\">\n <a href=\"#readme\">\n <img alt=\"Loguru logo\" src=\"https://raw.githubusercontent.com/Delgan/l"
},
{
"path": "docs/Makefile",
"chars": 604,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHI"
},
{
"path": "docs/_static/css/loguru.css",
"chars": 163,
"preview": ".wy-nav-content {\n /* By default it's hardcoded to 800px.\n We increase it a bit so code source fits without horizo"
},
{
"path": "docs/_static/js/copybutton.js",
"chars": 2772,
"preview": "$(document).ready(function() {\n /* Add a [>>>] button on the top-right corner of code samples to hide\n * the >>> "
},
{
"path": "docs/_templates/breadcrumbs.html",
"chars": 97,
"preview": "{%- extends \"sphinx_rtd_theme/breadcrumbs.html\" %}\n\n{% block breadcrumbs_aside %}\n{% endblock %}\n"
},
{
"path": "docs/_templates/layout.html",
"chars": 3828,
"preview": "{% extends '!layout.html' %}\n{% block document %}\n{{super()}}\n\n<div class=\"github-corner\">\n<svg width=\"120\" height=\"120\""
},
{
"path": "docs/api/logger.rst",
"chars": 121,
"preview": "``loguru.logger``\n=================\n\n.. automodule:: loguru._logger\n\n.. autoclass:: loguru._logger.Logger()\n :members:"
},
{
"path": "docs/api/type_hints.rst",
"chars": 4099,
"preview": ".. _type-hints:\n\nType Hints\n==========\n\n.. |str| replace:: :class:`str`\n.. |namedtuple| replace:: :func:`namedtuple<coll"
},
{
"path": "docs/api/type_hints_source.rst",
"chars": 139,
"preview": ":orphan:\n\n.. _type-hints-source:\n\nSource Code of Type Hints\n=========================\n\n.. include:: ../../loguru/__init_"
},
{
"path": "docs/api.rst",
"chars": 1396,
"preview": "API Reference\n=============\n\n.. automodule:: loguru\n\n.. toctree::\n :hidden:\n :includehidden:\n\n api/logger.rst\n a"
},
{
"path": "docs/conf.py",
"chars": 5655,
"preview": "\"\"\"Configuration file for the Sphinx documentation builder.\n\nThis file does only contain a selection of the most common "
},
{
"path": "docs/index.rst",
"chars": 249,
"preview": ".. include:: ../README.md\n :parser: myst_parser.sphinx_\n :end-before: <!-- end-of-readme-intro -->\n\nTable of Content"
},
{
"path": "docs/overview.rst",
"chars": 168,
"preview": "Overview\n========\n\n.. include:: ../README.md\n :parser: myst_parser.sphinx_\n :start-after: <!-- end-of-readme-intro -"
},
{
"path": "docs/project/changelog.rst",
"chars": 54,
"preview": "Changelog\n#########\n\n.. include:: ../../CHANGELOG.rst\n"
},
{
"path": "docs/project/contributing.rst",
"chars": 63,
"preview": "Contributing\n############\n\n.. include:: ../../CONTRIBUTING.rst\n"
},
{
"path": "docs/project/license.rst",
"chars": 71,
"preview": "License\n#######\n\n.. literalinclude:: ../../LICENSE\n :language: none\n"
},
{
"path": "docs/project.rst",
"chars": 131,
"preview": "Project Information\n===================\n\n.. toctree::\n\n project/contributing.rst\n project/license.rst\n project/cha"
},
{
"path": "docs/resources/migration.rst",
"chars": 19443,
"preview": "Switching from Standard Logging to Loguru\n=========================================\n\n.. highlight:: python3\n\n.. |getLogg"
},
{
"path": "docs/resources/recipes.rst",
"chars": 49598,
"preview": "Code Snippets and Recipes for Loguru\n====================================\n\n.. highlight:: python3\n\n.. |print| replace:: "
},
{
"path": "docs/resources/troubleshooting.rst",
"chars": 28675,
"preview": "Frequently Asked Questions and Troubleshooting Tips for Loguru\n========================================================="
},
{
"path": "docs/resources.rst",
"chars": 128,
"preview": "Help & Guides\n=============\n\n.. toctree::\n\n resources/migration.rst\n resources/troubleshooting.rst\n resources/reci"
},
{
"path": "loguru/__init__.py",
"chars": 627,
"preview": "\"\"\"\nThe Loguru library provides a pre-instanced logger to facilitate dealing with logging in Python.\n\nJust ``from loguru"
},
{
"path": "loguru/__init__.pyi",
"chars": 12041,
"preview": "import sys\nfrom asyncio import AbstractEventLoop\nfrom datetime import datetime, time, timedelta\nfrom logging import Hand"
},
{
"path": "loguru/_asyncio_loop.py",
"chars": 597,
"preview": "import asyncio\nimport sys\n\n\ndef load_loop_functions():\n if sys.version_info >= (3, 7):\n\n def get_task_loop(tas"
},
{
"path": "loguru/_better_exceptions.py",
"chars": 21732,
"preview": "import builtins\nimport inspect\nimport io\nimport keyword\nimport linecache\nimport os\nimport re\nimport sys\nimport sysconfig"
},
{
"path": "loguru/_colorama.py",
"chars": 2374,
"preview": "import builtins\nimport os\nimport sys\n\n\ndef should_colorize(stream):\n if stream is None:\n return False\n\n is_"
},
{
"path": "loguru/_colorizer.py",
"chars": 16264,
"preview": "import re\nfrom contextlib import contextmanager\nfrom string import Formatter\n\n\n@contextmanager\ndef try_formatting(*excep"
},
{
"path": "loguru/_contextvars.py",
"chars": 321,
"preview": "import sys\n\n\ndef load_contextvar_class():\n if sys.version_info >= (3, 7):\n from contextvars import ContextVar\n"
},
{
"path": "loguru/_ctime_functions.py",
"chars": 1527,
"preview": "import os\n\n\ndef load_ctime_functions():\n if os.name == \"nt\":\n import win32_setctime\n\n def get_ctime_win"
},
{
"path": "loguru/_datetime.py",
"chars": 6136,
"preview": "import re\nfrom calendar import day_abbr, day_name, month_abbr, month_name\nfrom datetime import datetime as datetime_\nfro"
},
{
"path": "loguru/_defaults.py",
"chars": 3009,
"preview": "from os import environ\n\n\ndef env(key, type_, default=None):\n if key not in environ:\n return default\n\n val ="
},
{
"path": "loguru/_error_interceptor.py",
"chars": 1107,
"preview": "import sys\nimport traceback\n\n\nclass ErrorInterceptor:\n def __init__(self, should_catch, handler_id):\n self._sh"
},
{
"path": "loguru/_file_sink.py",
"chars": 14951,
"preview": "import datetime\nimport decimal\nimport glob\nimport numbers\nimport os\nimport shutil\nimport string\nfrom functools import pa"
},
{
"path": "loguru/_filters.py",
"chars": 618,
"preview": "def filter_none(record):\n return record[\"name\"] is not None\n\n\ndef filter_by_name(record, parent, length):\n name = "
},
{
"path": "loguru/_get_frame.py",
"chars": 458,
"preview": "import sys\nfrom sys import exc_info\n\n\ndef get_frame_fallback(n):\n try:\n raise Exception\n except Exception:\n"
},
{
"path": "loguru/_handler.py",
"chars": 12591,
"preview": "import functools\nimport json\nimport multiprocessing\nimport os\nimport threading\nfrom contextlib import contextmanager\nfro"
},
{
"path": "loguru/_locks_machinery.py",
"chars": 1307,
"preview": "import os\nimport threading\nimport weakref\n\nif not hasattr(os, \"register_at_fork\"):\n\n def create_logger_lock():\n "
},
{
"path": "loguru/_logger.py",
"chars": 102589,
"preview": "\"\"\"Core logging functionalities of the `Loguru` library.\n\n.. References and links rendered by Sphinx are kept here as \"m"
},
{
"path": "loguru/_recattrs.py",
"chars": 7333,
"preview": "import pickle\nfrom collections import namedtuple\n\n\nclass RecordLevel:\n \"\"\"A class representing the logging level reco"
},
{
"path": "loguru/_simple_sinks.py",
"chars": 6758,
"preview": "import inspect\nimport logging\nimport weakref\n\nfrom ._asyncio_loop import get_running_loop, get_task_loop\n\n\nclass StreamS"
},
{
"path": "loguru/_string_parsers.py",
"chars": 9324,
"preview": "import datetime\nimport re\nfrom typing import Optional, Tuple\n\n\nclass Frequencies:\n \"\"\"Provide static methods to compu"
},
{
"path": "loguru/py.typed",
"chars": 0,
"preview": ""
},
{
"path": "pyproject.toml",
"chars": 5143,
"preview": "[build-system]\nbuild-backend = \"flit_core.buildapi\"\nrequires = [\"flit_core>=3,<4\"]\n\n[project]\nauthors = [{ name = \"Delga"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/conftest.py",
"chars": 12876,
"preview": "import asyncio\nimport builtins\nimport contextlib\nimport datetime\nimport io\nimport logging\nimport multiprocessing\nimport "
},
{
"path": "tests/exceptions/output/backtrace/chained_expression_direct.txt",
"chars": 1990,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/chained_expression_direct.py\", line 12, in"
},
{
"path": "tests/exceptions/output/backtrace/chained_expression_indirect.txt",
"chars": 1636,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/chained_expression_indirect.py\", line 11, "
},
{
"path": "tests/exceptions/output/backtrace/chaining_first.txt",
"chars": 1327,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/chaining_first.py\", line 26, in <module>\n "
},
{
"path": "tests/exceptions/output/backtrace/chaining_second.txt",
"chars": 1409,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/chaining_second.py\", line 38, in <module>\n"
},
{
"path": "tests/exceptions/output/backtrace/chaining_third.txt",
"chars": 1467,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/chaining_third.py\", line 46, in <module>\n "
},
{
"path": "tests/exceptions/output/backtrace/enqueue.txt",
"chars": 157,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/enqueue.py\", line 9, in <module>\n 1 / 0"
},
{
"path": "tests/exceptions/output/backtrace/enqueue_with_others_handlers.txt",
"chars": 179,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/enqueue_with_others_handlers.py\", line 26,"
},
{
"path": "tests/exceptions/output/backtrace/frame_values_backward.txt",
"chars": 456,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/frame_values_backward.py\", line 24, in <mo"
},
{
"path": "tests/exceptions/output/backtrace/frame_values_forward.txt",
"chars": 452,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/frame_values_forward.py\", line 24, in <mod"
},
{
"path": "tests/exceptions/output/backtrace/function.txt",
"chars": 711,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/function.py\", line 22, in <module>\n a()"
},
{
"path": "tests/exceptions/output/backtrace/head_recursion.txt",
"chars": 3852,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/head_recursion.py\", line 32, in <module>\n "
},
{
"path": "tests/exceptions/output/backtrace/missing_attributes_traceback_objects.txt",
"chars": 411,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/missing_attributes_traceback_objects.py\", "
},
{
"path": "tests/exceptions/output/backtrace/missing_lineno_frame_objects.txt",
"chars": 367,
"preview": "44: An error occurred\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/missing_lineno_frame_"
},
{
"path": "tests/exceptions/output/backtrace/nested.txt",
"chars": 963,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/nested.py\", line 17, in <module>\n a(0)\n"
},
{
"path": "tests/exceptions/output/backtrace/nested_chained_catch_up.txt",
"chars": 1428,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/nested_chained_catch_up.py\", line 22, in m"
},
{
"path": "tests/exceptions/output/backtrace/nested_decorator_catch_up.txt",
"chars": 748,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/nested_decorator_catch_up.py\", line 20, in"
},
{
"path": "tests/exceptions/output/backtrace/nested_explicit_catch_up.txt",
"chars": 742,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/nested_explicit_catch_up.py\", line 20, in "
},
{
"path": "tests/exceptions/output/backtrace/nested_wrapping.txt",
"chars": 842,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/nested_wrapping.py\", line 19, in <module>\n"
},
{
"path": "tests/exceptions/output/backtrace/no_tb.txt",
"chars": 42,
"preview": "Test:\nZeroDivisionError: division by zero\n"
},
{
"path": "tests/exceptions/output/backtrace/not_enough_arguments.txt",
"chars": 670,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/not_enough_arguments.py\", line 18, in <mod"
},
{
"path": "tests/exceptions/output/backtrace/raising_recursion.txt",
"chars": 9672,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/raising_recursion.py\", line 32, in <module"
},
{
"path": "tests/exceptions/output/backtrace/suppressed_expression_direct.txt",
"chars": 2356,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/suppressed_expression_direct.py\", line 16,"
},
{
"path": "tests/exceptions/output/backtrace/suppressed_expression_indirect.txt",
"chars": 2278,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/suppressed_expression_indirect.py\", line 1"
},
{
"path": "tests/exceptions/output/backtrace/tail_recursion.txt",
"chars": 3852,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/backtrace/tail_recursion.py\", line 29, in <module>\n "
},
{
"path": "tests/exceptions/output/backtrace/too_many_arguments.txt",
"chars": 640,
"preview": "\nTraceback (most recent call last):\n> File \"tests/exceptions/source/backtrace/too_many_arguments.py\", line 18, in <modul"
},
{
"path": "tests/exceptions/output/diagnose/assertion_error.txt",
"chars": 792,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1massertion_e"
},
{
"path": "tests/exceptions/output/diagnose/assertion_error_custom.txt",
"chars": 752,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1massertion_e"
},
{
"path": "tests/exceptions/output/diagnose/assertion_error_in_string.txt",
"chars": 586,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1massertion_e"
},
{
"path": "tests/exceptions/output/diagnose/attributes.txt",
"chars": 1839,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mattributes."
},
{
"path": "tests/exceptions/output/diagnose/chained_both.txt",
"chars": 1995,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mchained_bot"
},
{
"path": "tests/exceptions/output/diagnose/encoding.txt",
"chars": 810,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mencoding.py"
},
{
"path": "tests/exceptions/output/diagnose/global_variable.txt",
"chars": 704,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mglobal_vari"
},
{
"path": "tests/exceptions/output/diagnose/indentation_error.txt",
"chars": 500,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mindentation"
},
{
"path": "tests/exceptions/output/diagnose/keyword_argument.txt",
"chars": 1216,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mkeyword_arg"
},
{
"path": "tests/exceptions/output/diagnose/multilines_repr.txt",
"chars": 768,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mmultilines_"
},
{
"path": "tests/exceptions/output/diagnose/no_error_message.txt",
"chars": 692,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mno_error_me"
},
{
"path": "tests/exceptions/output/diagnose/parenthesis.txt",
"chars": 2967,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mparenthesis"
},
{
"path": "tests/exceptions/output/diagnose/source_multilines.txt",
"chars": 2559,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1msource_mult"
},
{
"path": "tests/exceptions/output/diagnose/source_strings.txt",
"chars": 537,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1msource_stri"
},
{
"path": "tests/exceptions/output/diagnose/syntax_error.txt",
"chars": 391,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1msyntax_erro"
},
{
"path": "tests/exceptions/output/diagnose/syntax_highlighting.txt",
"chars": 2679,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1msyntax_high"
},
{
"path": "tests/exceptions/output/diagnose/truncating.txt",
"chars": 691,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1mtruncating."
},
{
"path": "tests/exceptions/output/diagnose/unprintable_object.txt",
"chars": 366,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/diagnose/\u001b[0m\u001b[32m\u001b[1munprintable"
},
{
"path": "tests/exceptions/output/modern/decorate_async_generator.txt",
"chars": 5,
"preview": "Done\n"
},
{
"path": "tests/exceptions/output/modern/exception_formatting_async_generator.txt",
"chars": 1608,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/modern/exception_formatting_async_generator.py\", lin"
},
{
"path": "tests/exceptions/output/modern/exception_group_catch.txt",
"chars": 1550,
"preview": "\n + \u001b[33m\u001b[1mException Group Traceback (most recent call last):\u001b[0m\n |\n | File \"\u001b[32mtests/exceptions/source/modern"
},
{
"path": "tests/exceptions/output/modern/f_string.txt",
"chars": 1275,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/modern/\u001b[0m\u001b[32m\u001b[1mf_string.py\u001b["
},
{
"path": "tests/exceptions/output/modern/grouped_as_cause_and_context.txt",
"chars": 10156,
"preview": "\n + Exception Group Traceback (most recent call last):\n | File \"tests/exceptions/source/modern/grouped_as_cause_and_"
},
{
"path": "tests/exceptions/output/modern/grouped_max_depth.txt",
"chars": 17216,
"preview": "\n + Exception Group Traceback (most recent call last):\n | File \"tests/exceptions/source/modern/grouped_max_depth.py\""
},
{
"path": "tests/exceptions/output/modern/grouped_max_length.txt",
"chars": 3514,
"preview": "\n + Exception Group Traceback (most recent call last):\n | File \"tests/exceptions/source/modern/grouped_max_length.py"
},
{
"path": "tests/exceptions/output/modern/grouped_nested.txt",
"chars": 8512,
"preview": "\n + Exception Group Traceback (most recent call last):\n | File \"tests/exceptions/source/modern/grouped_nested.py\", l"
},
{
"path": "tests/exceptions/output/modern/grouped_simple.txt",
"chars": 5099,
"preview": "\n + Exception Group Traceback (most recent call last):\n | File \"tests/exceptions/source/modern/grouped_simple.py\", l"
},
{
"path": "tests/exceptions/output/modern/grouped_with_cause_and_context.txt",
"chars": 6401,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/modern/grouped_with_cause_and_context.py\", line 31, "
},
{
"path": "tests/exceptions/output/modern/match_statement.txt",
"chars": 1015,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/modern/\u001b[0m\u001b[32m\u001b[1mmatch_stateme"
},
{
"path": "tests/exceptions/output/modern/notes.txt",
"chars": 3466,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/modern/notes.py\", line 13, in <module>\n raise e\nV"
},
{
"path": "tests/exceptions/output/modern/positional_only_argument.txt",
"chars": 1243,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/modern/\u001b[0m\u001b[32m\u001b[1mpositional_on"
},
{
"path": "tests/exceptions/output/modern/type_hints.txt",
"chars": 1481,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/modern/\u001b[0m\u001b[32m\u001b[1mtype_hints.py"
},
{
"path": "tests/exceptions/output/modern/walrus_operator.txt",
"chars": 910,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n> File \"\u001b[32mtests/exceptions/source/modern/\u001b[0m\u001b[32m\u001b[1mwalrus_operat"
},
{
"path": "tests/exceptions/output/others/assertionerror_without_traceback.txt",
"chars": 116,
"preview": "\n\u001b[31m\u001b[1mAssertionError\u001b[0m\n\n\u001b[31m\u001b[1mAssertionError\u001b[0m\n\n\u001b[31m\u001b[1mAssertionError\u001b[0m\n\n\u001b[31m\u001b[1mAssertionError\u001b[0m\n"
},
{
"path": "tests/exceptions/output/others/broken_but_decorated_repr.txt",
"chars": 722,
"preview": "\nTraceback (most recent call last):\n\n> File \"tests/exceptions/source/others/broken_but_decorated_repr.py\", line 25, in <"
},
{
"path": "tests/exceptions/output/others/catch_as_context_manager.txt",
"chars": 172,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/catch_as_context_manager.py\", line 10, in <mo"
},
{
"path": "tests/exceptions/output/others/catch_as_decorator_with_parentheses.txt",
"chars": 291,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/catch_as_decorator_with_parentheses.py\", line"
},
{
"path": "tests/exceptions/output/others/catch_as_decorator_without_parentheses.txt",
"chars": 292,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/catch_as_decorator_without_parentheses.py\", l"
},
{
"path": "tests/exceptions/output/others/catch_as_function.txt",
"chars": 249,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/catch_as_function.py\", line 14, in <module>\n "
},
{
"path": "tests/exceptions/output/others/catch_message.txt",
"chars": 526,
"preview": "An error occurred (1):\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/catch_message.py\", line"
},
{
"path": "tests/exceptions/output/others/exception_formatting_coroutine.txt",
"chars": 1370,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/exception_formatting_coroutine.py\", line 20, "
},
{
"path": "tests/exceptions/output/others/exception_formatting_function.txt",
"chars": 1220,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/exception_formatting_function.py\", line 17, i"
},
{
"path": "tests/exceptions/output/others/exception_formatting_generator.txt",
"chars": 1314,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/exception_formatting_generator.py\", line 20, "
},
{
"path": "tests/exceptions/output/others/exception_in_property.txt",
"chars": 372,
"preview": "\nTraceback (most recent call last):\n\n File \"tests/exceptions/source/others/exception_in_property.py\", line 22, in <modu"
},
{
"path": "tests/exceptions/output/others/handler_formatting_with_context_manager.txt",
"chars": 236,
"preview": "__main__ handler_formatting_with_context_manager.py a 16\nTraceback (most recent call last):\n File \"tests/exceptions/sou"
},
{
"path": "tests/exceptions/output/others/handler_formatting_with_decorator.txt",
"chars": 338,
"preview": "__main__ handler_formatting_with_decorator.py <module> 20\nTraceback (most recent call last):\n File \"tests/exceptions/so"
},
{
"path": "tests/exceptions/output/others/level_name.txt",
"chars": 161,
"preview": "DEBUG | 10\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/level_name.py\", line 13, in a\n 1"
},
{
"path": "tests/exceptions/output/others/level_number.txt",
"chars": 166,
"preview": "Level 13 | 13\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/level_number.py\", line 13, in a\n"
},
{
"path": "tests/exceptions/output/others/message_formatting_with_context_manager.txt",
"chars": 236,
"preview": "__main__ message_formatting_with_context_manager.py a 10\nTraceback (most recent call last):\n File \"tests/exceptions/sou"
},
{
"path": "tests/exceptions/output/others/message_formatting_with_decorator.txt",
"chars": 338,
"preview": "__main__ message_formatting_with_decorator.py <module> 14\nTraceback (most recent call last):\n File \"tests/exceptions/so"
},
{
"path": "tests/exceptions/output/others/nested_with_reraise.txt",
"chars": 4500,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/nested_with_reraise.py\", line 20, in bar\n "
},
{
"path": "tests/exceptions/output/others/one_liner_recursion.txt",
"chars": 6074,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/one_liner_recursion.py\", line 14, in <module>"
},
{
"path": "tests/exceptions/output/others/recursion_error.txt",
"chars": 2531,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/recursion_error.py\", line 19, in <module>\n "
},
{
"path": "tests/exceptions/output/others/repeated_lines.txt",
"chars": 31083,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/repeated_lines.py\", line 22, in <module>\n "
},
{
"path": "tests/exceptions/output/others/syntaxerror_without_traceback.txt",
"chars": 388,
"preview": "\n File \"<string>\", line 1\n foo =\n ^\n\u001b[31m\u001b[1mSyntaxError\u001b[0m:\u001b[1m invalid syntax\u001b[0m\n\n File \"<string>\", lin"
},
{
"path": "tests/exceptions/output/others/sys_tracebacklimit.txt",
"chars": 2328,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/sys_tracebacklimit.py\", line 33, in f\n g()"
},
{
"path": "tests/exceptions/output/others/sys_tracebacklimit_negative.txt",
"chars": 148,
"preview": "\nZeroDivisionError: division by zero\n\nZeroDivisionError: division by zero\n\nZeroDivisionError: division by zero\n\nZeroDivi"
},
{
"path": "tests/exceptions/output/others/sys_tracebacklimit_none.txt",
"chars": 5024,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/sys_tracebacklimit_none.py\", line 55, in <mod"
},
{
"path": "tests/exceptions/output/others/sys_tracebacklimit_unset.txt",
"chars": 5068,
"preview": "\nTraceback (most recent call last):\n File \"tests/exceptions/source/others/sys_tracebacklimit_unset.py\", line 58, in <mo"
},
{
"path": "tests/exceptions/output/others/zerodivisionerror_without_traceback.txt",
"chars": 232,
"preview": "\n\u001b[31m\u001b[1mZeroDivisionError\u001b[0m:\u001b[1m division by zero\u001b[0m\n\n\u001b[31m\u001b[1mZeroDivisionError\u001b[0m:\u001b[1m division by zero\u001b[0m\n\n\u001b[3"
},
{
"path": "tests/exceptions/output/ownership/assertion_from_lib.txt",
"chars": 2926,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1massertion_"
},
{
"path": "tests/exceptions/output/ownership/assertion_from_local.txt",
"chars": 2190,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1massertion_"
},
{
"path": "tests/exceptions/output/ownership/callback.txt",
"chars": 4138,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mcallback.p"
},
{
"path": "tests/exceptions/output/ownership/catch_decorator.txt",
"chars": 3456,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mcatch_deco"
},
{
"path": "tests/exceptions/output/ownership/catch_decorator_from_lib.txt",
"chars": 3712,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mcatch_deco"
},
{
"path": "tests/exceptions/output/ownership/decorated_callback.txt",
"chars": 3103,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mdecorated_"
},
{
"path": "tests/exceptions/output/ownership/direct.txt",
"chars": 2555,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mdirect.py\u001b"
},
{
"path": "tests/exceptions/output/ownership/indirect.txt",
"chars": 3226,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mindirect.p"
},
{
"path": "tests/exceptions/output/ownership/string_lib.txt",
"chars": 3077,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mstring_lib"
},
{
"path": "tests/exceptions/output/ownership/string_source.txt",
"chars": 2920,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1mstring_sou"
},
{
"path": "tests/exceptions/output/ownership/syntaxerror.txt",
"chars": 2687,
"preview": "\n\u001b[33m\u001b[1mTraceback (most recent call last):\u001b[0m\n\n File \"\u001b[32mtests/exceptions/source/ownership/\u001b[0m\u001b[32m\u001b[1msyntaxerro"
},
{
"path": "tests/exceptions/source/backtrace/chained_expression_direct.py",
"chars": 642,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/chained_expression_indirect.py",
"chars": 360,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/chaining_first.py",
"chars": 393,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/chaining_second.py",
"chars": 516,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/chaining_third.py",
"chars": 624,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/enqueue.py",
"chars": 223,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, enqueue=True, format=\"\", colorize=False, b"
},
{
"path": "tests/exceptions/source/backtrace/enqueue_with_others_handlers.py",
"chars": 618,
"preview": "import sys\n\nfrom loguru import logger\n\n\ndef check_tb_sink(message):\n exception = message.record[\"exception\"]\n if e"
},
{
"path": "tests/exceptions/source/backtrace/frame_values_backward.py",
"chars": 237,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/frame_values_forward.py",
"chars": 237,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/function.py",
"chars": 317,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/head_recursion.py",
"chars": 466,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/missing_attributes_traceback_objects.py",
"chars": 1136,
"preview": "import sys\nfrom collections import namedtuple\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format="
},
{
"path": "tests/exceptions/source/backtrace/missing_lineno_frame_objects.py",
"chars": 1063,
"preview": "import sys\nfrom collections import namedtuple\n\nfrom loguru import logger\n\n\nlogger.remove()\nlogger.add(\n sys.stderr,\n "
},
{
"path": "tests/exceptions/source/backtrace/nested.py",
"chars": 457,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/nested_chained_catch_up.py",
"chars": 418,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=False"
},
{
"path": "tests/exceptions/source/backtrace/nested_decorator_catch_up.py",
"chars": 342,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=False"
},
{
"path": "tests/exceptions/source/backtrace/nested_explicit_catch_up.py",
"chars": 369,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=False"
},
{
"path": "tests/exceptions/source/backtrace/nested_wrapping.py",
"chars": 409,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/no_tb.py",
"chars": 335,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"{message}\", colorize=False, backtr"
},
{
"path": "tests/exceptions/source/backtrace/not_enough_arguments.py",
"chars": 353,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/raising_recursion.py",
"chars": 464,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/suppressed_expression_direct.py",
"chars": 693,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/suppressed_expression_indirect.py",
"chars": 480,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/tail_recursion.py",
"chars": 408,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/backtrace/too_many_arguments.py",
"chars": 339,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=False, backtrace=True,"
},
{
"path": "tests/exceptions/source/diagnose/assertion_error.py",
"chars": 261,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/assertion_error_custom.py",
"chars": 285,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/assertion_error_in_string.py",
"chars": 269,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/attributes.py",
"chars": 430,
"preview": "# fmt: off\nimport sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backt"
},
{
"path": "tests/exceptions/source/diagnose/chained_both.py",
"chars": 467,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/encoding.py",
"chars": 268,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/global_variable.py",
"chars": 296,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/indentation_error.py",
"chars": 316,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/keyword_argument.py",
"chars": 246,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/multilines_repr.py",
"chars": 341,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/no_error_message.py",
"chars": 258,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/parenthesis.py",
"chars": 626,
"preview": "# fmt: off\nimport sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backt"
},
{
"path": "tests/exceptions/source/diagnose/source_multilines.py",
"chars": 772,
"preview": "# fmt: off\nimport sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backt"
},
{
"path": "tests/exceptions/source/diagnose/source_strings.py",
"chars": 260,
"preview": "# fmt: off\nimport sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backt"
},
{
"path": "tests/exceptions/source/diagnose/syntax_error.py",
"chars": 251,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/syntax_highlighting.py",
"chars": 503,
"preview": "# fmt: off\nimport sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backt"
},
{
"path": "tests/exceptions/source/diagnose/truncating.py",
"chars": 248,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/diagnose/unprintable_object.py",
"chars": 307,
"preview": "import sys\n\nfrom loguru import logger\n\nlogger.remove()\nlogger.add(sys.stderr, format=\"\", colorize=True, backtrace=False,"
},
{
"path": "tests/exceptions/source/modern/decorate_async_generator.py",
"chars": 2694,
"preview": "from loguru import logger\nimport asyncio\nimport sys\n\nlogger.remove()\n\n# We're truly only testing whether the tests succe"
}
]
// ... and 105 more files (download for full content)
About this extraction
This page contains the full source code of the Delgan/loguru GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 305 files (993.1 KB), approximately 294.4k tokens, and a symbol index with 1342 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.