diff --git a/analog/define.py b/analog/define.py index 8e1f31d..c3eca44 100644 --- a/analog/define.py +++ b/analog/define.py @@ -68,8 +68,14 @@ class Meta: log_entry_class = type(str(log_model_name), (base_class,), class_dict) def add_log_entry( - self, message, identifier=None, kind="other", - user=None, extra=None, save=True, **kwargs + self, + message, + identifier=None, + kind="other", + user=None, + extra=None, + save=True, + **kwargs ): return log_entry_class.add_log_entry( target=self, diff --git a/analog/models.py b/analog/models.py index f833fa0..92d2e65 100644 --- a/analog/models.py +++ b/analog/models.py @@ -33,7 +33,8 @@ class BaseLogEntry(models.Model): created_on = models.DateTimeField(auto_now_add=True, editable=False) user = models.ForeignKey( getattr(settings, "AUTH_USER_MODEL", "auth.User"), - null=True, on_delete=models.PROTECT + null=True, + on_delete=models.PROTECT, ) message = models.CharField(max_length=256) identifier = models.CharField(max_length=64, blank=True) @@ -59,8 +60,13 @@ def save(self, *args, **kwargs): @classmethod def add_log_entry( cls, - target, message, identifier=None, kind="other", - user=None, extra=None, save=True, + target, + message, + identifier=None, + kind="other", + user=None, + extra=None, + save=True, **kwargs ): """ @@ -117,17 +123,17 @@ def add_log_entry( **kwargs ) - has_extra_field = ('extra' in cls._meta._forward_fields_map) + has_extra_field = "extra" in cls._meta._forward_fields_map if extra is not None: if not has_extra_field: raise NoExtraField( - 'The %r class has no `extra` field,' - 'but non-None extra was passed!' % cls + "The %r class has no `extra` field," + "but non-None extra was passed!" % cls ) - kwargs['extra'] = extra + kwargs["extra"] = extra if target is None: - kwargs.pop('target') + kwargs.pop("target") log_entry = cls(**kwargs) log_entry.clean() if save: diff --git a/analog/settings.py b/analog/settings.py index 7ad4bb4..0129bb8 100644 --- a/analog/settings.py +++ b/analog/settings.py @@ -9,7 +9,7 @@ "note": 4, "email": 5, "warning": 6, - "error": 7 + "error": 7, } _DEFAULT_KIND_LABELS = { @@ -20,7 +20,7 @@ "note": _("note"), "email": _("email"), "warning": _("warning"), - "error": _("error") + "error": _("error"), } diff --git a/analog/util.py b/analog/util.py index 0f23cc6..2f22fb3 100644 --- a/analog/util.py +++ b/analog/util.py @@ -18,7 +18,7 @@ def __getattr__(self, kind): :type kind: str :return: int """ - if kind.startswith('_'): + if kind.startswith("_"): return self.__getattribute__(kind) kind = str(kind).lower() if kind not in KINDS: diff --git a/analog_tests/__main__.py b/analog_tests/__main__.py index 1fec141..8ffc87c 100644 --- a/analog_tests/__main__.py +++ b/analog_tests/__main__.py @@ -5,4 +5,5 @@ sys.path.insert(0, os.path.realpath(os.path.dirname(__file__) + "/..")) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "analog_tests.settings") from django.core.management import execute_from_command_line + execute_from_command_line(sys.argv) diff --git a/analog_tests/models.py b/analog_tests/models.py index 79417a0..b43834d 100644 --- a/analog_tests/models.py +++ b/analog_tests/models.py @@ -26,8 +26,7 @@ class SecondLoggedModel(models.Model): pass -SecondLoggedModelLogEntry = define_log_model( - SecondLoggedModel, allow_null_target=True) +SecondLoggedModelLogEntry = define_log_model(SecondLoggedModel, allow_null_target=True) class ExtraLogEntry(BaseLogEntry): @@ -38,5 +37,4 @@ class ThirdLoggedModel(models.Model): pass -ThirdLoggedModelLogEntry = define_log_model( - ThirdLoggedModel, base_class=ExtraLogEntry) +ThirdLoggedModelLogEntry = define_log_model(ThirdLoggedModel, base_class=ExtraLogEntry) diff --git a/analog_tests/settings.py b/analog_tests/settings.py index 7be30c1..df39103 100644 --- a/analog_tests/settings.py +++ b/analog_tests/settings.py @@ -4,17 +4,8 @@ "django.contrib.contenttypes", "analog_tests", ) -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:' - } -} +DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} -ANALOG_KINDS = { - "custom_kind": 3010 -} +ANALOG_KINDS = {"custom_kind": 3010} -ANALOG_KIND_LABELS = { - "custom_kind": "very custom" -} +ANALOG_KIND_LABELS = {"custom_kind": "very custom"} diff --git a/analog_tests/test_analog.py b/analog_tests/test_analog.py index dcc9478..41bd948 100644 --- a/analog_tests/test_analog.py +++ b/analog_tests/test_analog.py @@ -4,8 +4,12 @@ from analog import BaseLogEntry, LogEntryKind, define_log_model from analog.exceptions import NoExtraField, UnknownLogKind from analog_tests.models import ( - FreeLogEntry, LoggedModel, LoggedModelLogEntry, SecondLoggedModel, - ThirdLoggedModel) + FreeLogEntry, + LoggedModel, + LoggedModelLogEntry, + SecondLoggedModel, + ThirdLoggedModel, +) class RandomModel(models.Model): @@ -23,6 +27,7 @@ def create_target_object(kind): if kind == "target": return LoggedModel.objects.create() else: + class FakeModel: pk = None @@ -52,13 +57,16 @@ def test_model_sanity(): @pytest.mark.django_db -@pytest.mark.parametrize("target_type,arg_type", [ - ("free", "kwargs"), - ("target", "args"), - ("target", "kwargs"), - # Note: ("free", "args") is ommitted here, because free log entries - # do not support kwargless api -]) +@pytest.mark.parametrize( + "target_type,arg_type", + [ + ("free", "kwargs"), + ("target", "args"), + ("target", "kwargs"), + # Note: ("free", "args") is ommitted here, because free log entries + # do not support kwargless api + ], +) def test_add_log_entry(target_type, arg_type): target_object = create_target_object(target_type) if target_object.pk: @@ -88,9 +96,7 @@ def test_log_entry_kind(target_object): @pytest.mark.django_db def test_log_mutation(target_object): - target_object.add_log_entry( - message="benign action", - kind=LogEntryKind.EDIT) + target_object.add_log_entry(message="benign action", kind=LogEntryKind.EDIT) log_entry = target_object.log_entries.last() log_entry.message = "sneak" with pytest.raises(ValueError): @@ -100,9 +106,8 @@ def test_log_mutation(target_object): @pytest.mark.django_db def test_user_logging(admin_user, target_object): target_object.add_log_entry( - message="audit", - kind=LogEntryKind.AUDIT, - user=admin_user) + message="audit", kind=LogEntryKind.AUDIT, user=admin_user + ) log_entry = target_object.log_entries.last() assert log_entry.user.is_superuser # we put an admin in @@ -119,14 +124,14 @@ def test_modify_before_save(target_object): @pytest.mark.django_db def test_extra_with_no_extra_field_is_an_error(target_object): with pytest.raises(NoExtraField): - target_object.add_log_entry(message="hi", extra={'henlo': 'fren'}) + target_object.add_log_entry(message="hi", extra={"henlo": "fren"}) @pytest.mark.django_db def test_extra_field(): target_object = ThirdLoggedModel.objects.create() - target_object.add_log_entry(message="hi", extra='hoi') - assert target_object.log_entries.last().extra == 'hoi' + target_object.add_log_entry(message="hi", extra="hoi") + assert target_object.log_entries.last().extra == "hoi" @pytest.mark.django_db @@ -147,10 +152,7 @@ def test_invalid_kinds(target_object): @pytest.mark.django_db def test_free_log_entries(): - fle = FreeLogEntry.add_log_entry( - target=None, - message="hello world" - ) + fle = FreeLogEntry.add_log_entry(target=None, message="hello world") assert fle.pk assert not fle.target assert FreeLogEntry.objects.last() == fle diff --git a/analog_tests/test_util.py b/analog_tests/test_util.py index 5536fef..0f35723 100644 --- a/analog_tests/test_util.py +++ b/analog_tests/test_util.py @@ -19,8 +19,8 @@ def test_unknown(): def test_internal(): kind_map = LogEntryKindMap() - assert kind_map.__module__ == 'analog.util' - assert LogEntryKindMap.__module__ == 'analog.util' + assert kind_map.__module__ == "analog.util" + assert LogEntryKindMap.__module__ == "analog.util" def test_internal_attribute_error(): @@ -28,4 +28,5 @@ def test_internal_attribute_error(): with pytest.raises(AttributeError) as excinfo: kind_map.__wrapped__ assert str(excinfo.value) == ( - "'LogEntryKindMap' object has no attribute '__wrapped__'") + "'LogEntryKindMap' object has no attribute '__wrapped__'" + ) diff --git a/docs/conf.py b/docs/conf.py index c180e83..8d37f95 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,41 +18,42 @@ # 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. -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("..")) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "analog_tests.settings") import django + django.setup() # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# 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 = [ - 'sphinx.ext.autodoc', + "sphinx.ext.autodoc", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +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' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'Django-Analog' -copyright = '2016, Anders Innovations' -author = 'Anders Innovations' +project = "Django-Analog" +copyright = "2016, Anders Innovations" +author = "Anders Innovations" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -61,19 +62,19 @@ def get_version(): - setupcfg_path = os.path.join(os.path.dirname(__file__), '..', 'setup.cfg') - with open(setupcfg_path, 'rb') as setupcfg: + setupcfg_path = os.path.join(os.path.dirname(__file__), "..", "setup.cfg") + with open(setupcfg_path, "rb") as setupcfg: for line in setupcfg.read().splitlines(): - if line.startswith(b'version = '): - return line.split(b'=', 1)[1].strip().decode('utf-8') - return '0.0.0' + if line.startswith(b"version = "): + return line.split(b"=", 1)[1].strip().decode("utf-8") + return "0.0.0" # The full version, including alpha/beta/rc tags. release = get_version() # The short X.Y version. -version = '.'.join(release.split('.')[:2]) +version = ".".join(release.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -84,37 +85,37 @@ def get_version(): # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -124,156 +125,155 @@ def get_version(): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' +html_theme = "alabaster" # 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 = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # 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'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'Django-Analogdoc' +htmlhelp_basename = "Django-Analogdoc" # -- 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', + # 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, 'Django-Analog.tex', 'Django-Analog Documentation', - 'Anders Innovations', 'manual'), + ( + master_doc, + "Django-Analog.tex", + "Django-Analog Documentation", + "Anders Innovations", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- 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, 'django-analog', 'Django-Analog Documentation', - [author], 1) -] +man_pages = [(master_doc, "django-analog", "Django-Analog Documentation", [author], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -282,19 +282,25 @@ def get_version(): # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Django-Analog', 'Django-Analog Documentation', - author, 'Django-Analog', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "Django-Analog", + "Django-Analog Documentation", + author, + "Django-Analog", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/setup.cfg b/setup.cfg index b99ccab..244d64a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,7 @@ requirements-dev = tox>=2.8 requirements-stylecheck = + black==20.8b1 flake8 flake8-isort flake8-print diff --git a/setup.py b/setup.py index e5e86e8..3553b4a 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ import setuptools -if __name__ == '__main__': +if __name__ == "__main__": setuptools.setup( - setup_requires=['setuptools>=30.3', 'setuptools-gitver'], - gitver=True) + setup_requires=["setuptools>=30.3", "setuptools-gitver"], gitver=True + ) diff --git a/tox.ini b/tox.ini index 645a1a5..a921ff2 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,9 @@ deps = skip_install = True basepython = python3.6 deps = -rrequirements-stylecheck.txt -commands = flake8 {posargs} +commands = + black --check + flake8 {posargs} [gh-actions] python =