Skip to content

Commit

Permalink
Fix issue with mutable dataclass fields. (#285)
Browse files Browse the repository at this point in the history
Closes issue #284.
  • Loading branch information
mcmtroffaes committed Feb 4, 2022
1 parent a8b9010 commit aab8ec1
Show file tree
Hide file tree
Showing 21 changed files with 168 additions and 145 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.7, 3.8, 3.9]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0-alpha.5']
pip-sphinx: [sphinx]
include:
- python-version: 3.9
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -5,6 +5,9 @@
fixing a violation against the docutils spec
(see issue #273, reported by rappdw, with additional input from brechtm).

* Fix mutable dataclass fields for Python 3.11 (see issue #284 and pull
request #285; reported and fixed by jamesjer)

2.4.1 (10 September 2021)
-------------------------

Expand Down
24 changes: 13 additions & 11 deletions doc/usage.rst
Expand Up @@ -759,26 +759,28 @@ Simply add the following code to your ``conf.py``:

.. code-block:: python
import dataclasses
from dataclasses import dataclass, field
import sphinxcontrib.bibtex.plugin
from sphinxcontrib.bibtex.style.referencing import BracketStyle
from sphinxcontrib.bibtex.style.referencing.author_year \
import AuthorYearReferenceStyle
my_bracket_style = BracketStyle(
left='(',
right=')',
)
def bracket_style() -> BracketStyle:
return BracketStyle(
left='(',
right=')',
)
@dataclasses.dataclass
@dataclass
class MyReferenceStyle(AuthorYearReferenceStyle):
bracket_parenthetical: BracketStyle = my_bracket_style
bracket_textual: BracketStyle = my_bracket_style
bracket_author: BracketStyle = my_bracket_style
bracket_label: BracketStyle = my_bracket_style
bracket_year: BracketStyle = my_bracket_style
bracket_parenthetical: BracketStyle = field(default_factory=bracket_style)
bracket_textual: BracketStyle = field(default_factory=bracket_style)
bracket_author: BracketStyle = field(default_factory=bracket_style)
bracket_label: BracketStyle = field(default_factory=bracket_style)
bracket_year: BracketStyle = field(default_factory=bracket_style)
sphinxcontrib.bibtex.plugin.register_plugin(
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Expand Up @@ -50,6 +50,8 @@ def plugin(plugin_name: str, mod_name: Optional[str] = None) -> str:
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Documentation',
Expand Down
26 changes: 12 additions & 14 deletions src/sphinxcontrib/bibtex/style/referencing/__init__.py
@@ -1,4 +1,4 @@
import dataclasses
from dataclasses import dataclass, field
from abc import ABC

import pybtex.plugin
Expand All @@ -19,15 +19,15 @@
from sphinxcontrib.bibtex.richtext import ReferenceInfo


@dataclasses.dataclass
@dataclass
class BaseReferenceStyle(ABC):
"""Base class for citation reference styles.
For consistency, all subclasses of this class must be decorated
as a :class:`dataclasses.dataclass`,
as a :class:`dataclass`,
and must provide a type annotation and default value for all attributes
(unless ``init=False`` is used, in which case they can be
initialized in :meth:`~dataclasses.dataclass.__post_init__`).
initialized in :meth:`~dataclass.__post_init__`).
This allows client code to instantiate any reference style
without needing to specify any arguments through the constructor.
"""
Expand Down Expand Up @@ -74,7 +74,7 @@ def format_references(
return style.outer(role_name, children).format()


@dataclasses.dataclass
@dataclass
class BracketStyle:
"""A class which provides brackets, as well as separators
and a function to facilitate formatting of the outer template.
Expand Down Expand Up @@ -116,7 +116,7 @@ def outer(
]


@dataclasses.dataclass
@dataclass
class PersonStyle:
"""A class providing additional data and helper functions
to facilitate formatting of person names.
Expand All @@ -127,7 +127,7 @@ class PersonStyle:

#: Plugin class instance used for formatting person names.
#: Automatically initialised from :attr:`style`.
style_plugin: "BaseNameStyle" = dataclasses.field(init=False)
style_plugin: "BaseNameStyle" = field(init=False)

#: Whether or not to abbreviate first names.
abbreviate: bool = True
Expand All @@ -142,8 +142,8 @@ class PersonStyle:
last_sep: Optional[Union["BaseText", str]] = ', and '

#: Abbreviation text if three or more persons.
other: Optional[Union["BaseText", str]] = \
Text(' ', Tag('em', 'et al.'))
other: Optional[Union["BaseText", str]] = field(
default_factory=lambda: Text(' ', Tag('em', 'et al.')))

def __post_init__(self):
self.style_plugin = pybtex.plugin.find_plugin(
Expand Down Expand Up @@ -173,18 +173,16 @@ def author_or_editor_or_title(self, full: bool) -> "Node":
)


@dataclasses.dataclass
@dataclass
class GroupReferenceStyle(BaseReferenceStyle):
"""Composes a group of reference styles into a single consistent style."""

#: List of style types.
styles: List[BaseReferenceStyle] \
= dataclasses.field(default_factory=list)
styles: List[BaseReferenceStyle] = field(default_factory=list)

#: Dictionary from role names to styles.
#: Automatically initialized from :attr:`styles`.
role_style: Dict[str, BaseReferenceStyle] \
= dataclasses.field(default_factory=dict)
role_style: Dict[str, BaseReferenceStyle] = field(default_factory=dict)

def __post_init__(self):
super().__post_init__()
Expand Down
16 changes: 8 additions & 8 deletions src/sphinxcontrib/bibtex/style/referencing/author_year.py
@@ -1,4 +1,4 @@
import dataclasses
from dataclasses import dataclass, field
from typing import Union, TYPE_CHECKING

from sphinxcontrib.bibtex.style.referencing import (
Expand All @@ -17,33 +17,33 @@
from pybtex.richtext import BaseText


@dataclasses.dataclass
@dataclass
class AuthorYearReferenceStyle(GroupReferenceStyle):
"""Textual or parenthetical reference by author-year,
or just by author, label, or year.
"""

#: Bracket style for textual citations (:cite:t: and variations).
bracket_textual: BracketStyle = BracketStyle()
bracket_textual: BracketStyle = field(default_factory=BracketStyle)

#: Bracket style for parenthetical citations
#: (:cite:p: and variations).
bracket_parenthetical: BracketStyle = BracketStyle()
bracket_parenthetical: BracketStyle = field(default_factory=BracketStyle)

#: Bracket style for author citations
#: (:cite:author: and variations).
bracket_author: BracketStyle = BracketStyle()
bracket_author: BracketStyle = field(default_factory=BracketStyle)

#: Bracket style for label citations
#: (:cite:label: and variations).
bracket_label: BracketStyle = BracketStyle()
bracket_label: BracketStyle = field(default_factory=BracketStyle)

#: Bracket style for year citations
#: (:cite:year: and variations).
bracket_year: BracketStyle = BracketStyle()
bracket_year: BracketStyle = field(default_factory=BracketStyle)

#: Person style (applies to all relevant citation commands).
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

#: Separator between author and year for parenthetical citations.
author_year_sep: Union["BaseText", str] = ', '
Expand Down
14 changes: 7 additions & 7 deletions src/sphinxcontrib/bibtex/style/referencing/basic_author_year.py
@@ -1,4 +1,4 @@
import dataclasses
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, List, Iterable, Union
from sphinxcontrib.bibtex.style.template import reference, join, year
from . import BaseReferenceStyle, BracketStyle, PersonStyle
Expand All @@ -8,15 +8,15 @@
from pybtex.style.template import Node


@dataclasses.dataclass
@dataclass
class BasicAuthorYearParentheticalReferenceStyle(BaseReferenceStyle):
"""Parenthetical reference by author-year."""

#: Bracket style.
bracket: BracketStyle = BracketStyle()
bracket: BracketStyle = field(default_factory=BracketStyle)

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

#: Separator between author and year.
author_year_sep: Union["BaseText", str] = ', '
Expand All @@ -39,15 +39,15 @@ def inner(self, role_name: str) -> "Node":
]


@dataclasses.dataclass
@dataclass
class BasicAuthorYearTextualReferenceStyle(BaseReferenceStyle):
"""Textual reference by author-year."""

#: Bracket style.
bracket: BracketStyle = BracketStyle()
bracket: BracketStyle = field(default_factory=BracketStyle)

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

#: Separator between text and reference.
text_reference_sep: Union["BaseText", str] = ' '
Expand Down
10 changes: 5 additions & 5 deletions src/sphinxcontrib/bibtex/style/referencing/basic_foot.py
@@ -1,4 +1,4 @@
import dataclasses
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, List, Iterable, Union
from sphinxcontrib.bibtex.style.template import footnote_reference, join
from . import BaseReferenceStyle, PersonStyle, BracketStyle
Expand All @@ -8,7 +8,7 @@
from pybtex.style.template import Node


@dataclasses.dataclass
@dataclass
class BasicFootParentheticalReferenceStyle(BaseReferenceStyle):
"""Parenthetical footnote reference."""

Expand All @@ -22,15 +22,15 @@ def inner(self, role_name: str) -> "Node":
return footnote_reference


@dataclasses.dataclass
@dataclass
class BasicFootTextualReferenceStyle(BaseReferenceStyle):
"""Textual footnote reference."""

#: Bracket style.
bracket: BracketStyle = BracketStyle()
bracket: BracketStyle = field(default_factory=BracketStyle)

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

#: Separator between text and reference.
text_reference_sep: Union["BaseText", str] = ' '
Expand Down
14 changes: 7 additions & 7 deletions src/sphinxcontrib/bibtex/style/referencing/basic_label.py
@@ -1,4 +1,4 @@
import dataclasses
from dataclasses import dataclass, field

from typing import TYPE_CHECKING, List, Iterable, Union
from sphinxcontrib.bibtex.style.template import reference, entry_label, join
Expand All @@ -9,17 +9,17 @@
from pybtex.style.template import Node


@dataclasses.dataclass
@dataclass
class BasicLabelParentheticalReferenceStyle(BaseReferenceStyle):
"""Reference by label if parenthetical,
and by author and label if textual.
"""

#: Bracket style.
bracket: BracketStyle = BracketStyle()
bracket: BracketStyle = field(default_factory=BracketStyle)

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

def role_names(self) -> Iterable[str]:
return [f'p{full_author}' for full_author in ['', 's']]
Expand All @@ -34,17 +34,17 @@ def inner(self, role_name: str) -> "Node":
return reference[entry_label]


@dataclasses.dataclass
@dataclass
class BasicLabelTextualReferenceStyle(BaseReferenceStyle):
"""Reference by label if parenthetical,
and by author and label if textual.
"""

#: Bracket style.
bracket: BracketStyle = BracketStyle()
bracket: BracketStyle = field(default_factory=BracketStyle)

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

#: Separator between text and reference.
text_reference_sep: Union["BaseText", str] = ' '
Expand Down
16 changes: 9 additions & 7 deletions src/sphinxcontrib/bibtex/style/referencing/basic_super.py
@@ -1,4 +1,4 @@
import dataclasses
from dataclasses import dataclass, field

from typing import TYPE_CHECKING, List, Iterable, Union
from pybtex.style.template import tag
Expand All @@ -10,17 +10,18 @@
from pybtex.style.template import Node


@dataclasses.dataclass
@dataclass
class BasicSuperParentheticalReferenceStyle(BaseReferenceStyle):
"""Reference by label superscript if parenthetical,
and by author and label superscript if textual.
"""

#: Bracket style. Left and right brackets are empty by default.
bracket: BracketStyle = BracketStyle(left='', right='', sep=',')
bracket: BracketStyle = field(
default_factory=lambda: BracketStyle(left='', right='', sep=','))

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

def role_names(self) -> Iterable[str]:
return [f'p{full_author}' for full_author in ['', 's']]
Expand All @@ -35,17 +36,18 @@ def inner(self, role_name: str) -> "Node":
return reference[entry_label]


@dataclasses.dataclass
@dataclass
class BasicSuperTextualReferenceStyle(BaseReferenceStyle):
"""Reference by label superscript if parenthetical,
and by author and label superscript if textual.
"""

#: Bracket style. Left and right brackets are empty by default.
bracket: BracketStyle = BracketStyle(left='', right='', sep=', ')
bracket: BracketStyle = field(
default_factory=lambda: BracketStyle(left='', right='', sep=', '))

#: Person style.
person: PersonStyle = PersonStyle()
person: PersonStyle = field(default_factory=PersonStyle)

#: Separator between text and reference.
text_reference_sep: Union["BaseText", str] = ''
Expand Down

0 comments on commit aab8ec1

Please sign in to comment.