-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
‼️ BREAKING: Change Token.attrs
to a dict
#144
Conversation
Codecov Report
@@ Coverage Diff @@
## master #144 +/- ##
==========================================
+ Coverage 96.38% 96.41% +0.03%
==========================================
Files 72 72
Lines 3205 3204 -1
==========================================
Hits 3089 3089
+ Misses 116 115 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
Should finally be the last thing before a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
I added some mainly type annotation related suggestions.
markdown_it/renderer.py
Outdated
# Fake token just to render attributes | ||
tmpToken = Token(type="", tag="", nesting=0, attrs=tmpAttrs) | ||
tmpToken = Token(type="", tag="", nesting=0, attrs=copy.copy(token.attrs)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dict also has dict.copy()
method if you want to avoid import copy
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done 👍
import warnings | ||
|
||
import attr | ||
|
||
|
||
def convert_attrs(value: Any) -> Any: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't the typing be something like
def convert_attrs(value: Any) -> Any: | |
def convert_attrs(value: Union[List[list], Dict[str, Union[str, float]], None]) -> Dict[str, Union[str, float]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say no, because conversion happens before validation in attrs, so technically it should be able to take anything and not fail
# Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]` | ||
attrs: Optional[List[list]] = attr.ib(default=None) | ||
# Html attributes. Note this differs from the upstream "list of lists" format | ||
attrs: Dict[str, Union[str, int, float]] = attr.ib( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int is implied by float so is not required. Is float used anywhere however? I know int is.
attrs: Dict[str, Union[str, int, float]] = attr.ib( | |
attrs: Dict[str, Union[str, float]] = attr.ib( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually would be less painful to just use Any
instead of the Union[str, float]]
in case someone gets an item directly without attrGet
. I've added suggestions also elsewhere to annotate read API with Any
and write API with Union[str, float]
because of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused why you suggest this, because isinstance(1, float)
equates to False
so int are certainly not a subclass of float.
As you mention, int
is used in core otherwise really I was thinking that perhaps it could just be str
only, since all these values always anyway have to be converted to strings in HTML attributes <div key="value">
.
I can't think of any reason, or HTML tag, that would require it not to be one of str
, int
, or float
and this restriction ensures that Token
is always "JSONable"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is in PEP 484 (https://www.python.org/dev/peps/pep-0484/#the-numeric-tower):
when an argument is annotated as having type float, an argument of type int is acceptable
The argument typing system is not one to one with isinstance
, it is more "duck typed".
it could just be str
I like this idea but only if we take the liberty to diverge from upstream and actually always set str
values only, i.e. convert before assigning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The argument typing system is not one to one with isinstance, it is more "duck typed".
Fair, although I think it is better for human readability/understanding to include int
specifically. Bit weird IMO, that typing should diverge from isinstance
but only if we take the liberty to diverge from upstream
yeh thats why I ultimately decided against it, just not worth the hassle
@@ -272,13 +272,13 @@ def tag(self) -> str: | |||
return self._attribute_token().tag | |||
|
|||
@property | |||
def attrs(self) -> Dict[str, Any]: | |||
def attrs(self) -> Dict[str, Union[str, int, float]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def attrs(self) -> Dict[str, Union[str, int, float]]: | |
def attrs(self) -> Dict[str, Any]: |
markdown_it/tree.py
Outdated
return dict(token_attrs) # type: ignore | ||
return self._attribute_token().attrs | ||
|
||
def attrGet(self, name: str) -> Any: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference would be to not add this method, but use attrs.get(name)
or attrs[name]
instead. Much more pythonic, and since this class is new API, there is nothing forcing us to copy the Token
classes attrs
setter and getter methods (which only exist because attrs
was not a mapping in JS upstream).
If you planning v1 already, could you have a look at #66 before releasing? Out of all the issues we have, I think that is the only one including anything breaking, so if we decide to do it would be nice to do so before v1 I think |
Co-authored-by: Taneli Hukkinen <hukkinj1@users.noreply.github.com>
As discussed in #102, upstream the list format is only used to guarantee order: markdown-it/markdown-it#142, but in Python 3.7+ dictionary order is now guaranteed by the specification (in Python 3.6 is also preserved as an implementation detail).
This change improves typing and performance.
To minimize how breaking this change is, auto-conversion is done on
Token
initiation, i.e. you can still useToken("type", "tag", 0, attrs=[["key", "value"]])
,and also
Token.as_dict(as_upstream=True)
converts the dict back tonull
/list
, so that they can still be directly compared to those produced in thedebug
tab of https://markdown-it.github.io/.One should anyhow generally use the
attrGet
,attrSet
,attrPush
andattrJoin
methodsto manipulate
Token.attrs
, which all have an identical signature to those upstream.I also added the
meta_serializer
option toToken.as_dict
, which now ensures that this method is always able to produce valid JSON.