-
Notifications
You must be signed in to change notification settings - Fork 168
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
Add typing to otfautohint #1719
base: develop
Are you sure you want to change the base?
Add typing to otfautohint #1719
Conversation
As you're probably realizing this has a lot of formatting related problems (flake8), some of which are due to edits for line length that you've removed. |
@simoncozens thanks for this contribution. It looks like there are a number of linter failures that are blocking the tests from running. I recommend that you run |
Yeah, sorry, I was assuming the code formatting was black/PEP8 so my editor runs black on save to keep things tidy. I'll try and put things back into your preferred style. |
I think I would prefer a submission with the substantive changes and then (perhaps) a different submission with the large amount of stylistic changes. Or perhaps just skip the latter? Best to either mesh one's contributions with the existing style or talk in advance about what you want to change and why. Edit: Ninja-ed. |
@josh-hadley Merging this would presumably require additional |
To be honest, it doesn't currently work with pytype. I started working with pytype, and as I added annotations I found myself in situations where a method returned an instance of its own class, which needs the |
e8c89f7
to
f82dd2b
Compare
f82dd2b
to
5e5a0e3
Compare
Coverage tests are sad because apparently you can't use |
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.
Some preliminary review feedback
from fontTools.pens.basePen import BasePen | ||
|
||
import logging | ||
|
||
# from .hintstate import glyphHintState |
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.
May not need this commented import?
"""Returns True if a and b are not close enough to be considered equal""" | ||
return abs(a - b) >= factor | ||
|
||
|
||
class pt(tuple): |
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 did make pt a tuple consciously, so that they are immutable and reusable and for certain other benefits. Can you talk about the reasons for your converting them to standard objects?
@@ -224,14 +238,14 @@ def __mul__(self, other): | |||
Returns a new pt object with this object's coordinates multiplied by | |||
a scalar value | |||
""" | |||
if not isinstance(other, numbers.Number): | |||
if not isinstance(other, (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.
I guess I'm OK with this, although it goes a bit against the spirit of the interface. In theory, at least, someone might want to call these operations with some other number-like parameter.
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.
numbers.Number
includes complex
, which I think you should probably exclude.
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.
Fair enough.
@@ -1407,7 +1439,7 @@ def toStems(self, data): | |||
for i in range(len(data) // 2): | |||
low = high + data[i * 2] | |||
high = low + data[i * 2 + 1] | |||
sl.append(stem(low, high)) | |||
sl.append(stem(low, high)) # pytype: disable=wrong-arg-count |
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.
What's the thinking about keeping these relative to pytype not working with this code (at least for now)
@@ -428,7 +462,7 @@ def handleOverlap(self): | |||
else: | |||
return self.isMulti | |||
|
|||
def addSegment(self, fr, to, loc, pe1, pe2, typ, desc, mid=False): | |||
def addSegment(self, fr, to, loc, pe1: Optional[pathElement], pe2: Optional[pathElement], typ, desc, mid=False) -> None: |
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 has flake8 problems too, doesn't it?
"""Return point cp3 adjusted relative to cp2 by CPFrac""" | ||
return (cp3 - cp2) * (1.0 - self.CPfrac) + cp2 | ||
|
||
def CPTo(self, cp0, cp1): | ||
def CPTo(self, cp0, cp1) -> 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.
What is the purpose of the Any return type notations? Does it help compared with just leaving them off?
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.
Python typing is progressive, and basically, I haven't finished adding all the annotations. But if you add any type notation, a method gets checked as best it can be, but with no type notation at all you don't get any checking or inferencing.
if seg0.isBBox(): | ||
if seg0.isGBBox(): | ||
pbs = self.glyph.getBounds(None) | ||
else: | ||
# I don't believe this can be reached, because |
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 probably just so obscure a corner case that none of our tests exercise it and no one has hit it yet, so we didn't catch the bug. I'll take a look at some point.
seg0.isBBox() and not seg0.isGBBox()
implies that the stems for seg0 were generated from the bounding box of the segment itself because nothing else worked. Only very unusual splines will have that problem.
One reason we ported This PR is both an external contribution and an effort to make further contributions easier, so in that sense it's what we want and were hoping for. Still, there is a basic concern about keeping these annotations from rotting. Some users will have environments that will make good use of the typing. Others will just be editing the files and running the tests and may have no idea what this stuff is or, if they do, how to change the annotations as the code changes. Obviously some sort of verifier that can be run as part of the GitHub checks would be ideal, but it sounds like Do you have thoughts on this? Basically, how to maintain types in Python assuming a wide variety of users with different levels of experience and tooling? Also: Keep in mind that we haven't been doing this internally, so it make take a while just to digest the implications of typed Python on our end. |
FWIW annotations are pretty useless unless they are actually checked by a typechecker |
ok, well.. self-documentation sure, but they then tend to rot as @skef noted. |
We can certainly get mypy set up in the CI, and add a disable comment to turn off the Self checks. |
Description
otfautohint is a complex piece of code, and it's easy to get lost; it's even easier to make mistakes. Having typing information available makes it easier to catch such mistakes, and allows editors with Python type inferencing (Visual Code with pylance, Sublime Text with pyright, etc.) to provide type hints for variables and methods. This in turn helps to find typos, dead code, and so on.
Checklist:
I have added test code and data to prove that my code functions correctly(N/A)I have made corresponding changes to the documentation(N/A)