Skip to content
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

Avoid mutation of TTFont to fix issues with concurrent tests #4835

Merged
merged 2 commits into from
Sep 17, 2024

Conversation

Hoolean
Copy link
Contributor

@Hoolean Hoolean commented Sep 16, 2024

Although the mutation is reversed, it could still have interfered with TTFont instances being used concurrently by other tests.

Before undrafting, I will test to see if this fixes #4834.

(even if not, the change may prevent similar issues in the future, and so we may wish to merge anyway)

Checklist

  • Update CHANGELOG.md
  • Wait for the tests to pass
  • Request a review

Although this mutation is reversed, it could still interfere with TTFont
instances being used concurrently by other tests.

Possible fix for fonttools#4834
@Hoolean
Copy link
Contributor Author

Hoolean commented Sep 16, 2024

Failing pytest appears to be unrelated; has a dependency bumped somewhere?

@Hoolean Hoolean marked this pull request as ready for review September 16, 2024 14:36
@Hoolean
Copy link
Contributor Author

Hoolean commented Sep 16, 2024

Undrafting as the PR appears to fix the race condition for me locally (although I may have just gotten lucky)

# This function mutates the TTF; deep copy to avoid this, and so avoid
# issues with concurrent tests that also use ttFont.
# See https://github.com/fonttools/fontbakery/issues/4834
ttFont = deepcopy(ttFont)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll admit that I'm a bit unsure about this. By assigning a deepcopy of ttFont to itself aren' t we still overwriting the original object? I suppose we may need to use a different name. Something like my_ttFont = deepcopy(ttFont). Does it make sense? I'm not sure if that's exactly how this works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that Python gives function parameters their own independent variable to store their reference to an object, but agree that it's 100% worth double-checking; I had a look just now to be safe, and it looks like we're all good 🎉

from dataclasses import dataclass

@dataclass
class Wrapper:
    value: int

def replacer(parameter: Wrapper) -> None:
    parameter = Wrapper(value=2)

outside = Wrapper(value=1)
print(outside)  # prints Wrapper(value=1)
replacer(outside)
print(outside)  # prints Wrapper(value=1) ✅

I considered giving the variable a new name, but thought that reusing it would avoid extra refactoring and would have the bonus of making it impossible for the rest of the function to use the pre-copied object by mistake.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I've just pushed a few commits changing the names, but I had not seen these messages before doing so. I think I'll revert my additional commit, then.

Failing pytest appears to be unrelated;

Just to be sure we're on the same page, what was the pytest failure message in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, thanks for taking a look Felipe! 🚀

Yes, reverting sounds good - the test that I am seeing ERROR locally is unreachable_glyphs, which appears to be the one upsetting the CI too, with full error:

AttributeError: 'TTFont' object has no attribute 'glyphOrder'. Did you mean: 'getGlyphOrder'?

I thought it was unrelated before, but now I see that it is one of the checks that we have touched in this PR.

I think what might be happening is that the check previously populated .glyphOrder as a side-effect, but now that we deepcopy it is not happening any more; this is probably a good thing, so I think the pytest just needs to be updated to call .getGlyphOrder() instead of trying to access the raw property to accommodate this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turned out to be a little more complicated because the test relied on certain context to remove a glyph; I think I have a full fix with fe7ab5e though (fuller explanation there too)

@felipesanches felipesanches force-pushed the avoid-ttfont-mutation branch 2 times, most recently from f96b1da to 8958834 Compare September 16, 2024 19:15
The previous approach relied on the `glyphOrder` property being
populated as a side-effect of the "unreachable-glyphs" check, but this
no longer happens now that the accidental mutation in the check has been
removed.

This commit avoids using the internal property entirely by using the
`setGlyphOrder()` function, and ensuring that the font is fully loaded
prior to this to avoid the glyph being referenced during a later load
after it has already been removed from the order.

If this causes issues again, it may be more resilient for us to use
fontTools' subsetter library to implement this pytest.

(PR fonttools#4835)
@felipesanches felipesanches merged commit 400fdab into fonttools:main Sep 17, 2024
52 checks passed
@Hoolean Hoolean deleted the avoid-ttfont-mutation branch September 18, 2024 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multithreading causes errors in version 0.13.0a0
2 participants