Name | logging-strict JSON |
Version |
1.5.0
JSON |
| download |
home_page | None |
Summary | logging.config yaml strict typing and editable |
upload_time | 2025-01-18 11:13:23 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.9 |
license | Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS |
keywords |
strictyaml
pre-commit
yaml
logging.config
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
.. For details: https://github.com/msftcangoblowm/logging-strict/blob/master/NOTICE.txt
logging-strict
===============
logging.config yaml Strict typing and editable
| |kit| |license| |versions|
| |test-status| |codecov| |quality-status| |docs|
| |stars| |mastodon-msftcangoblowm|
For logging.config yaml files, logging-strict does the following:
- Editable logging configuration
While running a Python app, some arbritary package, out of no
where, decides to log an informational warning. Within a multiprocessing
worker (aka heavy background processing), these logging warnings go
from annoying --> disruptive.
The best can do is *adapt to survive*. Make this situation quickly
solvable by adjusting the app's logging configuration.
asyncio is an example package which bleeds informational logging warnings
- curates
Intention is to have all the valid logging.config yaml in one place
- validator
logging_strict comes with a logging.config yaml validator. So can
check the editted yaml file. Supports pre-commit
- validates against a strictyaml schema
The schema is specifically tailored for the logging.handlers
As long as the yaml is valid, will have the data types
logging.handlers expect
- exports package data
Alternative to pkgutil.get_data
Export data files using a pattern rather than one file at a time
.. PYVERSIONS
* Python 3.9 through 3.12, and 3.13.0a3 and up.
**New in 1.5.x:**
registry logging_strict.yml;
**New in 1.4.x:**
yaml logger package_name placeholder; noninstalled package raise ImportError;
fix package name to valid dotted path (#3);
Why?
------
logging.config is more often than not hardcoded within a package's
source code. Removing logging.config from the source code and into
an exported yaml config file, a package becomes adaptable to
unforeseen unexpected bleeding of logging messages.
When a bleed occurs, open the exported logging.config yaml file. Add
the offending package to the ``loggers`` section or if already there, increase
the logging level.
For example, for asyncio, adjust logging level from
logging.WARNING --> logging.ERROR
Bye bye disruptive informational logging warning messages.
logging_strict comes with a logging.config yaml validator. So can
check the editted yaml file.
On app|worker restart, the logging configuration changes take effect.
Exporting -- when
------------------
Exports occur before the logging.config yaml files are needed. There
are two process types: worker and app
When an app is run, it exports the app logging configuration.
Right before a ProcessPool runs, it exports the worker logging configuration.
Right before a thread or ThreadPool runs, G'd and Darwin sit down to decide
which calamity will befall you. Best to avoid that cuz Python logging module is
thread-safe. Changes to the logging.config in one thread affects them all
and those changes last as long as the app runs.
Safe means safe to remove you from the gene pool. Would be a great name for a
horror movie. Don't be in that movie.
Exporting -- where/what
------------------------
Export location (on linux): ``$HOME/.local/share/[package name]/``
This is xdg user data dir and the configuration is per package.
Python logging configurations' cascade!
Whats exported?
- one for the app
- At least one, for the multiprocessing workers
If a user|coder edits and makes a change, undo'ing those changes would be
considered quite rude, minimally, poor ettiquette.
So that gauntlets stay on and package authors live long fulfilling peaceful
uneventful lives, overwrite existing logging config yaml files never
happens. Although fully capable, just absolutely refuses to do so!
If confident no changes have been made, can manually delete (unlink).
There will be no need for gauntlets, can safely put those away.
Upgrade path
--------------
*How to upgrade a particular logging.config yaml file?*
Best to increment the version and switch the code base to use the latest version
Custom changes should be upstreamed.
*Preferred the previous version*
There currently isn't a means to change which logging.config yaml file
a package uses.
This sounds like a job for user preference database, gschema. Not yet
implemented
Validation
-----------
logging.handlers, each, expects parameters to have the correct data type.
yaml package strictyaml, default data type is str, for other types, the function
variable name and type must be known (and supported) beforehand.
For custom (handlers, filters, and formatters) functions, there is no
way to know beforehand the parameter name **and therefore** the data type,
parameter type will become str
(Assuming the virtual environment is activated)
Within source code (tree)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. image:: https://raw.githubusercontent.com/msftcangoblowm/logging-strict/master/docs/_static/validate_flavor_asz.gif
:alt: validation of package logging.config yaml files
:width: 1000px
:height: 500px
.. code:: console
logging_strict_validate_yaml
.. code:: text
Processed: 4 / 4
Success / fail: 4 / 0
last (3): ~/Downloads/logging_strict/src/logging_strict/configs/mp_1_asz.worker.logging.config.yaml
.. code:: console
logging_strict_validate_yaml --category worker
.. code:: text
Processed: 3 / 3
Success / fail: 3 / 0
last (2): ~/Downloads/logging_strict/src/logging_strict/configs/mp_1_asz.worker.logging.config.yaml
.. code:: console
logging_strict_validate_yaml --category app
.. code:: text
Processed: 1 / 1
Success / fail: 1 / 0
last (0): ~/Downloads/logging_strict/src/logging_strict/configs/textual_1_asz.app.logging.config.yaml
.. note:: Two workers are just ordinary yaml files
Withinin logging_strict source tree, `bad_idea/folder*/*` are two folders,
each contains one file.
Although valid yaml, these are not actual logging.config yaml files.
Just there for testing purposes
The total `*.logging.config.yaml` file count and total
`*.worker.logging.config.yaml` are both thrown off by `+2`
Within xdg user data dir
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: console
logging_strict_validate_yaml $HOME/.local/share/logging_strict/ worker
Processed: 1 / 1
Success / fail: 1 / 0
last (0): ~/.local/share/logging_strict/mp_1_asz.worker.logging.config.yaml
.. code:: console
logging_strict_validate_yaml $HOME/.local/share/logging_strict/ app
Processed: 1 / 1
Success / fail: 1 / 0
last (0): ~/.local/share/logging_strict/textual_1_asz.app.logging.config.yaml
pre-commit
------------
Locally
.. code:: text
repos:
- repo: local
hooks:
- id: validate-logging-config-yaml
name: validate-logging-config-yaml
entry: logging_strict_validate_yaml
language: python
require_serial: true
pass_filenames: false
Normal usage
.. code:: text
repos:
- repo: https://github.com/msftcangoblowm/logging-strict
rev: 0.1.0
hooks:
- id: validate-logging-config-yaml
name: validate-logging-config-yaml
entry: logging_strict_validate_yaml
language: python
require_serial: true
pass_filenames: false
install
--------
You know how to use pip. This is not that.
Lets discuss integrating logging-strict into your app and history
dust binning hardcoded logging configuration.
UI
~~~
An entrypoint boilerplate should be structured like, or slightly
differently for an async app
.. code:: text
def _process_args(): ...
def main():
d_out = _process_args()
...
# app logging config stuff <--- here!
app = MyApp() # <-- not within here
...
if __name__ = "__main__":
main()
This entrypoint is testable. If the argparsing is done within main,
it's time to refactor and rework the entrypoint.
An Entrypoint have defined and **documented** exit codes. Besides for
``--help|-h``, never prints a message
logging.config yaml -- within logging_strict
"""""""""""""""""""""""""""""""""""""""""""""
.. code:: text
from logging_strict.constants import
from logging_strict import ui_yaml_curated, LoggingState
genre = "textual"
version_no = "1"
flavor = "asz" # < -- Yet unpublished testing UI package
package_start_relative_folder = ""
LoggingState().is_state_app = True
ui_yaml_curated(
genre,
flavor,
version_no=version_no,
package_start_relative_folder=package_start_relative_folder, # <-- narrows the search
)
logging.config yaml -- within another package
""""""""""""""""""""""""""""""""""""""""""""""
.. code:: text
from mypackage.constants import urpackagename, package_data_folder_start
from logging_strict import setup_ui_other, LoggingState
genre = "textual"
flavor = "asz" # < -- Yet unpublished testing UI package
version_no = "1"
package_start_relative_folder = ""
LoggingState().is_state_app = True
setup_ui_other(
urpackagename, # <-- Would have been better to curate within logging_strict
package_data_folder_start,
genre,
flavor,
version_no=version_no,
package_start_relative_folder=package_start_relative_folder,
)
- package
Package within which the `*.[app|worker].logging.config.yaml` files
reside.
Which is preferrably within logging_strict. So all the logging.config yaml
in the universe need not be duplicated to the point where it appears
to compete with fiat currency.
- package_data_folder_start
Within that package, which is the package base folder somewhere
within the folder tree lies the `*.[app|worker].logging.config.yaml`
files. This is a str, not a relative path.
One folder name. Does not assume the folder is called ``data``. Does assume
data files are within at least one folder. And if not? G'd and Darwin. Or
panties are bound to get twisted.
- category
The function name indicates the purpose. To setup ``logging.config`` for
a worker, call function, ``setup_worker``
- genre
From a main app's POV, genre is the UI framework such as: pyside or textual
From a worker's POV, genre hints at the implementation:
mp (multiprocessing) or rabbitmq, ...
- flavor
Like a one word brand name to a particular logging.config yaml file. For the
initially used the brand, ``asz``, a Python testing UI app
- version_no
When changes have to be made either: Increment
the version by 1 or if purpose is different, fork a new flavor
If no flavor, version pertains to the genre
- package_start_relative_folder
Relative to package_data_folder_start, narrows search.
For example,
``bad_idea/folder0/`` and ``bad_idea/folder1`` both contains,
``mp_1_shared.worker.logging.config.yaml``. Which one?
package_data_folder_start is ``bad_idea``, not ``configs`` or ``data``.
package_start_relative_folder could be ``folder0``. Which is enough
to identify the exact file.
LoggingState
"""""""""""""
A Singleton holding logging state. To know whether or not, run by app
or from cli
(there is also the issue of run by: coverage, unittest, or pytest)
If run from app, and testing app component, logging is redirected to
`textual.logging.TextualHandler` and shouldn't be changed.
If run from cli, and testing app component, logging is redirected to
`logging.handlers.StreamHandler`, not TextualHandler
During testing, the app and workers are run in all three scenerios.
From coverage, from unittest, and from asz.
While the logging handler is TextualHandler, changing to StreamHandler
would be bad. LoggingState aim is to avoid that.
Why would want to do testing from an UI?
- **Speeeeeeeeeed!**
Minimizing keypresses or actions required to run commands
- Associating unittests to code modules
Which unittest(s) must be run to get 100% coverage for a particular
code module?
Without organization, can only imagine that there must always be a 1:1
ratio between unittest and code module. And if not, the unittests
folder is just a jumbled mess. And which unittests matter for a
particular code module is unknown.
**Give a brother a clue!**
A clear easily maintainable verifiable guide is necessary.
worker
-------
This is a 2 step process.
- Step 1 -- entrypoint
Extracts yaml from package, validates, then passes as str to the worker process
- Step 2 -- worker
yaml str --> logging.config.dictConfig
within entrypoint
~~~~~~~~~~~~~~~~~~
The ProcessPool (not ThreadPool) worker is isolated within it's own
process. So the dirty nature of logging configuration has no effect
on other processes.
logging.config yaml file within package, logging_strict
.. code:: text
from logging_strict import worker_yaml_curated
genre = "mp"
flavor = "asz"
str_yaml = worker_yaml_curated(genre, flavor)
logging.config yaml file within another package
.. code:: text
from logging_strict import worker_yaml_curated
package = "someotherpackage"
package_data_folder_start = "data" # differs so need to check this folder name
genre = "mp"
flavor = "asz"
str_yaml = setup_worker_other(package, package_data_folder_start, genre, flavor)
within worker
~~~~~~~~~~~~~~
entrypoint passes str_yaml to the (ProcessPool) worker. A worker calls
`setup_logging_yaml` with the yaml str
.. code:: text
from logging_strict import setup_logging_yaml
setup_logging_yaml(str_yaml)
To learn more about building UI apps that have `multiprocessing.pool.ProcessPool`
workers, check out the `asz` source code
Public API
-----------
.. code:: text
from logging_strict import (
LoggingConfigCategory,
LoggingState,
LoggingYamlType,
setup_ui_other,
ui_yaml_curated,
setup_worker_other,
worker_yaml_curated,
setup_logging_yaml,
LoggingStrictError,
LoggingStrictPackageNameRequired,
LoggingStrictPackageStartFolderNameRequired,
LoggingStrictProcessCategoryRequired,
LoggingStrictGenreRequired,
)
- LoggingConfigCategory
tl;dr; ^^ won't need this ^^
Process categories Enum. Iterate over the Enum values, using class
method, `categories`.
`strict_logging` public methods are convenience functions for class,
`strict_logging.logging_api.LoggingConfigYaml`. If LoggingConfigYaml
used directly, choose one of the LoggingConfigCategory values to
pass as param, category.
- LoggingYamlType
tl;dr; ^^ won't need this ^^
Useful only during strict type checking. class LoggingConfigYaml
implements LoggingYamlType interface and is a direct subclass
- LoggingStrictError
logging_strict catch all Exception. Base type of other exceptions.
Implements ValueError
The other exceptions are self explanatory. When creating worker
entrypoints, can set exit codes based on which exception occurred.
Whats strictyaml?
------------------
Unfortunately yaml spec is too broad, allowing undesirable complexity, which
are a frequent cause of security issues. Read more:
- `[why] <https://hitchdev.com/strictyaml/why/>`_
- `[why nots] <https://hitchdev.com/strictyaml/why-not/>`_
strictyaml (`[docs] <https://hitchdev.com/strictyaml/>`_) mitigates
yaml security issues:
- by only supporting a subset of the yaml spec
- type-safe YAML parsing and validation against a schema
In our case, specialized to support the built-in Python
logging.handlers and adaptable enough to support custom
handlers, filters, and formatters
.. |test-status| image:: https://github.com/msftcangoblowm/logging-strict/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
:target: https://github.com/msftcangoblowm/logging-strict/actions/workflows/testsuite.yml
:alt: Test suite status
.. |quality-status| image:: https://github.com/msftcangoblowm/logging-strict/actions/workflows/quality.yml/badge.svg?branch=master&event=push
:target: https://github.com/msftcangoblowm/logging-strict/actions/workflows/quality.yml
:alt: Quality check status
.. |docs| image:: https://readthedocs.org/projects/logging-strict/badge/?version=latest&style=flat
:target: https://logging-strict.readthedocs.io/
:alt: Documentation
.. |kit| image:: https://img.shields.io/pypi/v/logging-strict
:target: https://pypi.org/project/logging-strict/
:alt: PyPI status
.. |versions| image:: https://img.shields.io/pypi/pyversions/logging-strict.svg?logo=python&logoColor=FBE072
:target: https://pypi.org/project/logging-strict/
:alt: Python versions supported
.. |license| image:: https://img.shields.io/github/license/msftcangoblowm/logging-strict
:target: https://pypi.org/project/logging-strict/blob/master/LICENSE.txt
:alt: License
.. |stars| image:: https://img.shields.io/github/stars/msftcangoblowm/logging-strict.svg?logo=github
:target: https://github.com/msftcangoblowm/logging-strict/stargazers
:alt: GitHub stars
.. |mastodon-msftcangoblowm| image:: https://img.shields.io/mastodon/follow/112019041247183249
:target: https://mastodon.social/@msftcangoblowme
:alt: msftcangoblowme on Mastodon
.. |codecov| image:: https://codecov.io/gh/msftcangoblowm/logging-strict/graph/badge.svg?token=HCBC74IABR
:target: https://codecov.io/gh/msftcangoblowm/logging-strict
:alt: logging-strict coverage percentage
Raw data
{
"_id": null,
"home_page": null,
"name": "logging-strict",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "strictyaml, pre-commit, yaml, logging.config",
"author": null,
"author_email": "Dave Faulkmore <faulkmore@telegram.org>",
"download_url": "https://files.pythonhosted.org/packages/a4/29/a05a904efec9a6528dc751792600eb224ff5ced9439ef69b53b268a61a6f/logging_strict-1.5.0.tar.gz",
"platform": null,
"description": ".. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0\n.. For details: https://github.com/msftcangoblowm/logging-strict/blob/master/NOTICE.txt\n\nlogging-strict\n===============\n\nlogging.config yaml Strict typing and editable\n\n| |kit| |license| |versions|\n| |test-status| |codecov| |quality-status| |docs|\n| |stars| |mastodon-msftcangoblowm|\n\nFor logging.config yaml files, logging-strict does the following:\n\n- Editable logging configuration\n\n While running a Python app, some arbritary package, out of no\n where, decides to log an informational warning. Within a multiprocessing\n worker (aka heavy background processing), these logging warnings go\n from annoying --> disruptive.\n\n The best can do is *adapt to survive*. Make this situation quickly\n solvable by adjusting the app's logging configuration.\n\n asyncio is an example package which bleeds informational logging warnings\n\n- curates\n\n Intention is to have all the valid logging.config yaml in one place\n\n- validator\n\n logging_strict comes with a logging.config yaml validator. So can\n check the editted yaml file. Supports pre-commit\n\n- validates against a strictyaml schema\n\n The schema is specifically tailored for the logging.handlers\n\n As long as the yaml is valid, will have the data types\n logging.handlers expect\n\n- exports package data\n\n Alternative to pkgutil.get_data\n\n Export data files using a pattern rather than one file at a time\n\n.. PYVERSIONS\n\n* Python 3.9 through 3.12, and 3.13.0a3 and up.\n\n**New in 1.5.x:**\nregistry logging_strict.yml;\n\n**New in 1.4.x:**\n\nyaml logger package_name placeholder; noninstalled package raise ImportError;\nfix package name to valid dotted path (#3);\n\nWhy?\n------\n\nlogging.config is more often than not hardcoded within a package's\nsource code. Removing logging.config from the source code and into\nan exported yaml config file, a package becomes adaptable to\nunforeseen unexpected bleeding of logging messages.\n\nWhen a bleed occurs, open the exported logging.config yaml file. Add\nthe offending package to the ``loggers`` section or if already there, increase\nthe logging level.\n\nFor example, for asyncio, adjust logging level from\nlogging.WARNING --> logging.ERROR\n\nBye bye disruptive informational logging warning messages.\n\nlogging_strict comes with a logging.config yaml validator. So can\ncheck the editted yaml file.\n\nOn app|worker restart, the logging configuration changes take effect.\n\nExporting -- when\n------------------\n\nExports occur before the logging.config yaml files are needed. There\nare two process types: worker and app\n\nWhen an app is run, it exports the app logging configuration.\n\nRight before a ProcessPool runs, it exports the worker logging configuration.\n\nRight before a thread or ThreadPool runs, G'd and Darwin sit down to decide\nwhich calamity will befall you. Best to avoid that cuz Python logging module is\nthread-safe. Changes to the logging.config in one thread affects them all\nand those changes last as long as the app runs.\n\nSafe means safe to remove you from the gene pool. Would be a great name for a\nhorror movie. Don't be in that movie.\n\nExporting -- where/what\n------------------------\n\nExport location (on linux): ``$HOME/.local/share/[package name]/``\n\nThis is xdg user data dir and the configuration is per package.\nPython logging configurations' cascade!\n\nWhats exported?\n\n- one for the app\n\n- At least one, for the multiprocessing workers\n\nIf a user|coder edits and makes a change, undo'ing those changes would be\nconsidered quite rude, minimally, poor ettiquette.\n\nSo that gauntlets stay on and package authors live long fulfilling peaceful\nuneventful lives, overwrite existing logging config yaml files never\nhappens. Although fully capable, just absolutely refuses to do so!\n\nIf confident no changes have been made, can manually delete (unlink).\n\nThere will be no need for gauntlets, can safely put those away.\n\nUpgrade path\n--------------\n\n*How to upgrade a particular logging.config yaml file?*\n\nBest to increment the version and switch the code base to use the latest version\n\nCustom changes should be upstreamed.\n\n*Preferred the previous version*\n\nThere currently isn't a means to change which logging.config yaml file\na package uses.\n\nThis sounds like a job for user preference database, gschema. Not yet\nimplemented\n\nValidation\n-----------\n\nlogging.handlers, each, expects parameters to have the correct data type.\n\nyaml package strictyaml, default data type is str, for other types, the function\nvariable name and type must be known (and supported) beforehand.\n\nFor custom (handlers, filters, and formatters) functions, there is no\nway to know beforehand the parameter name **and therefore** the data type,\nparameter type will become str\n\n(Assuming the virtual environment is activated)\n\nWithin source code (tree)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. image:: https://raw.githubusercontent.com/msftcangoblowm/logging-strict/master/docs/_static/validate_flavor_asz.gif\n :alt: validation of package logging.config yaml files\n :width: 1000px\n :height: 500px\n\n.. code:: console\n\n logging_strict_validate_yaml\n\n.. code:: text\n\n Processed: 4 / 4\n Success / fail: 4 / 0\n last (3): ~/Downloads/logging_strict/src/logging_strict/configs/mp_1_asz.worker.logging.config.yaml\n\n.. code:: console\n\n logging_strict_validate_yaml --category worker\n\n.. code:: text\n\n Processed: 3 / 3\n Success / fail: 3 / 0\n last (2): ~/Downloads/logging_strict/src/logging_strict/configs/mp_1_asz.worker.logging.config.yaml\n\n.. code:: console\n\n logging_strict_validate_yaml --category app\n\n.. code:: text\n\n Processed: 1 / 1\n Success / fail: 1 / 0\n last (0): ~/Downloads/logging_strict/src/logging_strict/configs/textual_1_asz.app.logging.config.yaml\n\n.. note:: Two workers are just ordinary yaml files\n\n Withinin logging_strict source tree, `bad_idea/folder*/*` are two folders,\n each contains one file.\n\n Although valid yaml, these are not actual logging.config yaml files.\n Just there for testing purposes\n\n The total `*.logging.config.yaml` file count and total\n `*.worker.logging.config.yaml` are both thrown off by `+2`\n\nWithin xdg user data dir\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code:: console\n\n logging_strict_validate_yaml $HOME/.local/share/logging_strict/ worker\n\nProcessed: 1 / 1\nSuccess / fail: 1 / 0\nlast (0): ~/.local/share/logging_strict/mp_1_asz.worker.logging.config.yaml\n\n.. code:: console\n\n logging_strict_validate_yaml $HOME/.local/share/logging_strict/ app\n\nProcessed: 1 / 1\nSuccess / fail: 1 / 0\nlast (0): ~/.local/share/logging_strict/textual_1_asz.app.logging.config.yaml\n\npre-commit\n------------\n\nLocally\n\n.. code:: text\n\n repos:\n - repo: local\n hooks:\n - id: validate-logging-config-yaml\n name: validate-logging-config-yaml\n entry: logging_strict_validate_yaml\n language: python\n require_serial: true\n pass_filenames: false\n\nNormal usage\n\n.. code:: text\n\n repos:\n - repo: https://github.com/msftcangoblowm/logging-strict\n rev: 0.1.0\n hooks:\n - id: validate-logging-config-yaml\n name: validate-logging-config-yaml\n entry: logging_strict_validate_yaml\n language: python\n require_serial: true\n pass_filenames: false\n\ninstall\n--------\n\nYou know how to use pip. This is not that.\n\nLets discuss integrating logging-strict into your app and history\ndust binning hardcoded logging configuration.\n\nUI\n~~~\n\nAn entrypoint boilerplate should be structured like, or slightly\ndifferently for an async app\n\n.. code:: text\n\n def _process_args(): ...\n\n def main():\n d_out = _process_args()\n ...\n # app logging config stuff <--- here!\n app = MyApp() # <-- not within here\n ...\n\n if __name__ = \"__main__\":\n main()\n\nThis entrypoint is testable. If the argparsing is done within main,\nit's time to refactor and rework the entrypoint.\n\nAn Entrypoint have defined and **documented** exit codes. Besides for\n``--help|-h``, never prints a message\n\nlogging.config yaml -- within logging_strict\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\n.. code:: text\n\n from logging_strict.constants import\n from logging_strict import ui_yaml_curated, LoggingState\n\n genre = \"textual\"\n version_no = \"1\"\n flavor = \"asz\" # < -- Yet unpublished testing UI package\n package_start_relative_folder = \"\"\n\n LoggingState().is_state_app = True\n ui_yaml_curated(\n genre,\n flavor,\n version_no=version_no,\n package_start_relative_folder=package_start_relative_folder, # <-- narrows the search\n )\n\nlogging.config yaml -- within another package\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\n.. code:: text\n\n from mypackage.constants import urpackagename, package_data_folder_start\n from logging_strict import setup_ui_other, LoggingState\n\n genre = \"textual\"\n flavor = \"asz\" # < -- Yet unpublished testing UI package\n version_no = \"1\"\n package_start_relative_folder = \"\"\n\n LoggingState().is_state_app = True\n setup_ui_other(\n urpackagename, # <-- Would have been better to curate within logging_strict\n package_data_folder_start,\n genre,\n flavor,\n version_no=version_no,\n package_start_relative_folder=package_start_relative_folder,\n )\n\n- package\n\n Package within which the `*.[app|worker].logging.config.yaml` files\n reside.\n\n Which is preferrably within logging_strict. So all the logging.config yaml\n in the universe need not be duplicated to the point where it appears\n to compete with fiat currency.\n\n- package_data_folder_start\n\n Within that package, which is the package base folder somewhere\n within the folder tree lies the `*.[app|worker].logging.config.yaml`\n files. This is a str, not a relative path.\n\n One folder name. Does not assume the folder is called ``data``. Does assume\n data files are within at least one folder. And if not? G'd and Darwin. Or\n panties are bound to get twisted.\n\n- category\n\n The function name indicates the purpose. To setup ``logging.config`` for\n a worker, call function, ``setup_worker``\n\n- genre\n\n From a main app's POV, genre is the UI framework such as: pyside or textual\n\n From a worker's POV, genre hints at the implementation:\n mp (multiprocessing) or rabbitmq, ...\n\n- flavor\n\n Like a one word brand name to a particular logging.config yaml file. For the\n initially used the brand, ``asz``, a Python testing UI app\n\n- version_no\n\n When changes have to be made either: Increment\n the version by 1 or if purpose is different, fork a new flavor\n\n If no flavor, version pertains to the genre\n\n- package_start_relative_folder\n\n Relative to package_data_folder_start, narrows search.\n\n For example,\n\n ``bad_idea/folder0/`` and ``bad_idea/folder1`` both contains,\n ``mp_1_shared.worker.logging.config.yaml``. Which one?\n\n package_data_folder_start is ``bad_idea``, not ``configs`` or ``data``.\n package_start_relative_folder could be ``folder0``. Which is enough\n to identify the exact file.\n\nLoggingState\n\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nA Singleton holding logging state. To know whether or not, run by app\nor from cli\n\n(there is also the issue of run by: coverage, unittest, or pytest)\n\nIf run from app, and testing app component, logging is redirected to\n`textual.logging.TextualHandler` and shouldn't be changed.\n\nIf run from cli, and testing app component, logging is redirected to\n`logging.handlers.StreamHandler`, not TextualHandler\n\nDuring testing, the app and workers are run in all three scenerios.\n\nFrom coverage, from unittest, and from asz.\n\nWhile the logging handler is TextualHandler, changing to StreamHandler\nwould be bad. LoggingState aim is to avoid that.\n\nWhy would want to do testing from an UI?\n\n- **Speeeeeeeeeed!**\n\nMinimizing keypresses or actions required to run commands\n\n- Associating unittests to code modules\n\nWhich unittest(s) must be run to get 100% coverage for a particular\ncode module?\n\nWithout organization, can only imagine that there must always be a 1:1\nratio between unittest and code module. And if not, the unittests\nfolder is just a jumbled mess. And which unittests matter for a\nparticular code module is unknown.\n\n**Give a brother a clue!**\n\nA clear easily maintainable verifiable guide is necessary.\n\nworker\n-------\n\nThis is a 2 step process.\n\n- Step 1 -- entrypoint\n\n Extracts yaml from package, validates, then passes as str to the worker process\n\n- Step 2 -- worker\n\n yaml str --> logging.config.dictConfig\n\nwithin entrypoint\n~~~~~~~~~~~~~~~~~~\n\nThe ProcessPool (not ThreadPool) worker is isolated within it's own\nprocess. So the dirty nature of logging configuration has no effect\non other processes.\n\nlogging.config yaml file within package, logging_strict\n\n.. code:: text\n\n from logging_strict import worker_yaml_curated\n\n genre = \"mp\"\n flavor = \"asz\"\n\n str_yaml = worker_yaml_curated(genre, flavor)\n\nlogging.config yaml file within another package\n\n.. code:: text\n\n from logging_strict import worker_yaml_curated\n\n package = \"someotherpackage\"\n package_data_folder_start = \"data\" # differs so need to check this folder name\n\n genre = \"mp\"\n flavor = \"asz\"\n\n str_yaml = setup_worker_other(package, package_data_folder_start, genre, flavor)\n\n\nwithin worker\n~~~~~~~~~~~~~~\n\nentrypoint passes str_yaml to the (ProcessPool) worker. A worker calls\n`setup_logging_yaml` with the yaml str\n\n.. code:: text\n\n from logging_strict import setup_logging_yaml\n\n setup_logging_yaml(str_yaml)\n\n\nTo learn more about building UI apps that have `multiprocessing.pool.ProcessPool`\nworkers, check out the `asz` source code\n\nPublic API\n-----------\n\n.. code:: text\n\n from logging_strict import (\n LoggingConfigCategory,\n LoggingState,\n LoggingYamlType,\n setup_ui_other,\n ui_yaml_curated,\n setup_worker_other,\n worker_yaml_curated,\n setup_logging_yaml,\n LoggingStrictError,\n LoggingStrictPackageNameRequired,\n LoggingStrictPackageStartFolderNameRequired,\n LoggingStrictProcessCategoryRequired,\n LoggingStrictGenreRequired,\n )\n\n- LoggingConfigCategory\n\n tl;dr; ^^ won't need this ^^\n\n Process categories Enum. Iterate over the Enum values, using class\n method, `categories`.\n\n `strict_logging` public methods are convenience functions for class,\n `strict_logging.logging_api.LoggingConfigYaml`. If LoggingConfigYaml\n used directly, choose one of the LoggingConfigCategory values to\n pass as param, category.\n\n- LoggingYamlType\n\n tl;dr; ^^ won't need this ^^\n\n Useful only during strict type checking. class LoggingConfigYaml\n implements LoggingYamlType interface and is a direct subclass\n\n- LoggingStrictError\n\n logging_strict catch all Exception. Base type of other exceptions.\n Implements ValueError\n\n The other exceptions are self explanatory. When creating worker\n entrypoints, can set exit codes based on which exception occurred.\n\nWhats strictyaml?\n------------------\n\nUnfortunately yaml spec is too broad, allowing undesirable complexity, which\nare a frequent cause of security issues. Read more:\n\n- `[why] <https://hitchdev.com/strictyaml/why/>`_\n\n- `[why nots] <https://hitchdev.com/strictyaml/why-not/>`_\n\nstrictyaml (`[docs] <https://hitchdev.com/strictyaml/>`_) mitigates\nyaml security issues:\n\n- by only supporting a subset of the yaml spec\n\n- type-safe YAML parsing and validation against a schema\n\n In our case, specialized to support the built-in Python\n logging.handlers and adaptable enough to support custom\n handlers, filters, and formatters\n\n.. |test-status| image:: https://github.com/msftcangoblowm/logging-strict/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push\n :target: https://github.com/msftcangoblowm/logging-strict/actions/workflows/testsuite.yml\n :alt: Test suite status\n.. |quality-status| image:: https://github.com/msftcangoblowm/logging-strict/actions/workflows/quality.yml/badge.svg?branch=master&event=push\n :target: https://github.com/msftcangoblowm/logging-strict/actions/workflows/quality.yml\n :alt: Quality check status\n.. |docs| image:: https://readthedocs.org/projects/logging-strict/badge/?version=latest&style=flat\n :target: https://logging-strict.readthedocs.io/\n :alt: Documentation\n.. |kit| image:: https://img.shields.io/pypi/v/logging-strict\n :target: https://pypi.org/project/logging-strict/\n :alt: PyPI status\n.. |versions| image:: https://img.shields.io/pypi/pyversions/logging-strict.svg?logo=python&logoColor=FBE072\n :target: https://pypi.org/project/logging-strict/\n :alt: Python versions supported\n.. |license| image:: https://img.shields.io/github/license/msftcangoblowm/logging-strict\n :target: https://pypi.org/project/logging-strict/blob/master/LICENSE.txt\n :alt: License\n.. |stars| image:: https://img.shields.io/github/stars/msftcangoblowm/logging-strict.svg?logo=github\n :target: https://github.com/msftcangoblowm/logging-strict/stargazers\n :alt: GitHub stars\n.. |mastodon-msftcangoblowm| image:: https://img.shields.io/mastodon/follow/112019041247183249\n :target: https://mastodon.social/@msftcangoblowme\n :alt: msftcangoblowme on Mastodon\n.. |codecov| image:: https://codecov.io/gh/msftcangoblowm/logging-strict/graph/badge.svg?token=HCBC74IABR\n :target: https://codecov.io/gh/msftcangoblowm/logging-strict\n :alt: logging-strict coverage percentage\n",
"bugtrack_url": null,
"license": "Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. \"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. \"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. \"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. \"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License. \"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. \"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. \"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). \"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. \"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\" \"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ",
"summary": "logging.config yaml strict typing and editable",
"version": "1.5.0",
"project_urls": {
"Changes": "https://raw.githubusercontent.com/msftcangoblowm/logging-strict/master/CHANGES.rst",
"Documentation": "https://logging-strict.readthedocs.io/en/stable",
"Issue tracker": "https://github.com/msftcangoblowm/logging-strict/issues",
"Mastodon": "https://mastodon.social/@msftcangoblowme",
"PyPI Releases": "https://pypi.org/project/logging-strict",
"Source code": "https://github.com/msftcangoblowm/logging-strict"
},
"split_keywords": [
"strictyaml",
" pre-commit",
" yaml",
" logging.config"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "7e48dbbbf26395d53bad69db1af3167e3230760a9c3e71705fa9c2c1b323444c",
"md5": "69c02dfc64f6b831027cf89b403a80ac",
"sha256": "c7b01d54bcd4f79cfca498766efe4778dd71438a819f326509f6472c13599cb7"
},
"downloads": -1,
"filename": "logging_strict-1.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "69c02dfc64f6b831027cf89b403a80ac",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 104607,
"upload_time": "2025-01-18T11:13:20",
"upload_time_iso_8601": "2025-01-18T11:13:20.608487Z",
"url": "https://files.pythonhosted.org/packages/7e/48/dbbbf26395d53bad69db1af3167e3230760a9c3e71705fa9c2c1b323444c/logging_strict-1.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a429a05a904efec9a6528dc751792600eb224ff5ced9439ef69b53b268a61a6f",
"md5": "30e60911d1721cf142317b72d813fd1d",
"sha256": "9e8e64767246003fdd3dcbb5f16c96be01745e14e213142c4fee3703bc3b57dc"
},
"downloads": -1,
"filename": "logging_strict-1.5.0.tar.gz",
"has_sig": false,
"md5_digest": "30e60911d1721cf142317b72d813fd1d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 474636,
"upload_time": "2025-01-18T11:13:23",
"upload_time_iso_8601": "2025-01-18T11:13:23.762314Z",
"url": "https://files.pythonhosted.org/packages/a4/29/a05a904efec9a6528dc751792600eb224ff5ced9439ef69b53b268a61a6f/logging_strict-1.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-18 11:13:23",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "msftcangoblowm",
"github_project": "logging-strict",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "logging-strict"
}