-
Notifications
You must be signed in to change notification settings - Fork 28
Implement MessageContext.format #67
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
Implement MessageContext.format #67
Conversation
and other bug fixes
…mbers Thanks Babel!
Thanks again, @spookylukey, for putting so much effort and work into this PR. It's a pleasure to read the code and documentation. I'm still reviewing it, and I'd like to propose the following landing plan. The current This repository currently uses the structure suggested by https://packaging.python.org/guides/packaging-namespace-packages/ which allows other distribution packages to use the same I'd be most happy if you agreed to be the maintainer of this new repo and its corresponding package on PyPI. With separate versioning, the bundle development could proceed in its own pace, tied to a specific version of the As far as this PR goes, I suggest that we keep it open for now so that we have a space to discuss the code. Expect review comments from @Pike, @zbraniecki and myself next week. When the review is completed, let's close this PR and land the code in the new repo. Let me know if this sounds like a good plan. Thanks again! |
Or we could land it here but change the directory structure such that each distribution lives in its own directory, together with its
I think it would allow us to publish each distribution independently, e.g. |
part_count = attr.ib(default=0) | ||
|
||
|
||
def resolve(context, message, args, errors=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.
After a while away, I have my own comment here!
Instead of passing context
to resolve, I think it would probably be nicer if we passed the things from MessageContext
that are actually needed, namely:
- the message and term dicts, possibly combined into a single dict.
- locale
- use_isolating
- functions
This would be a bit more verbose, but it would have the effect that the resolver code no longer needs to access internals of MessageContext
(e.g. ._babel_locale
). Also plural_form_for_number
would then come off MessageContext
(which isn't a great home for it) and go somewhere better.
It would also make this code similar to the equivalent code in the compiler implementation
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 think that passing a context
is quite safe here and I saw Stas using that convention around
I do agree that a single dict for all IDs would be useful, but instead of passing a locale, I'd recommend passing a locale fallback chain as provided to the FluentBundle
constructor.
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.
@zbraniecki - the problem with passing a locale fallback chain is that resolve
would then have to convert this to a Babel locale object every time it is called, in other words for every single call to format
(even though it will always return the same thing), rather than doing it once up front.
fluent/context.py
Outdated
return self._messages[message_id] | ||
|
||
@cachedproperty | ||
def plural_form_for_number(self): |
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 doesn't seem a great place for this function, see my comment here - https://github.com/projectfluent/python-fluent/pull/67/files#r231033619
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.
yeah, I'd prefer not to put it here if possible. Happy to see it in a follow up.
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.
Maybe we could put it as a prop in the constructor?
extra_requires = ['singledispatch>=3.4'] | ||
else: | ||
extra_requires = [] | ||
|
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 think this will give us issues in creating wheels - we'll need different wheels for Python 2, Python 3 < 3.4 and Python 3 >= 3.4 due to different requirements, which is a bit annoying, but possible, as per PEP 425.
@stasm I'm not experienced with creating namespace packages, so not sure what to recommend. Both of the options you gave seem sensible to me. I initially leant to having a single repo with two setup.py files. However, if we want to have independent development, then it is annoying if PRs for the parser get snagged on the fact that the build now fails due to Regarding repo names, including |
Would this still be a problem if the bundle package always used published versions of the syntax package?
We could still have I think I'm leaning towards keeping everything in a single repository. We already do this with It's also possible to use a Lastly, it's easier to perform housekeeping tasks in a single repo. Updating READMEs, linting code, etc. can all be done with a single PR. |
That's a good solution, I hadn't thought of that. The other things you mentioned sound fine, if you think it would be easier overall to have a single repo let's go with that. In fact if we decide to split at some point we can still do that, and it will be easier than trying to merge I imagine. |
I really think we should go for separate repos. Automation is going to be way easier to set up, and it'll be clear what tests to run, and what to blame for bustages. Also, it took me a a bit to find that you could actually refer to subdirectory for VCS install links, https://pip.pypa.io/en/stable/reference/pip_install/#vcs-support. First time I come across them. I would find having separate bug/issues lists probably also beneficial. Even more so as you can move issues between repos now. Happy to help setting up the repository structure for a corresponding repo. A few highlevel comments I remember from looking at the code. I'd throw away python 3.4 and older support, I think everybody these days is going for 2.7 and 3.5+? We also talked about perf a while back, in the meantime I came across https://github.com/benfred/py-spy, which also works on macs :-) Running the perf tests, I saw that the dispatch code was very hot. I've toyed around removing that for plain text messages, but I think the top-level dispatch on message/term was still hurting. Probably also something that can be lifted? I did entertain the idea to have the parser generate custom and possibly optimized ASTs, where you could pass in a "resolver" AST module, which would have I also had a glance on the new dependencies. In the meantime, I've started using |
@Pike - regarding performance, I've done almost nothing in this branch, because I put all my effort into the compiler implementation instead - this is always going to be way faster, and if you want a fast implementation you should be using that instead. For the interpreter/evaluator clarity is most important IMO. Of course if there are easy performance gains that we should implement those - a single special case for plain text messages at the entry point probably wouldn't hurt clarity much. |
in line with projectfluent#67 (comment)
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.
Generally speaking this looks great!
I'm comfortable landing this as my comments are rather minor and can be resolved in follow ups and most of them are implementation details that we can iron our over time.
What I think should happen soon is the naming scheme update to match the JS/Rust implementations, but even that can comfortably land in a follow up (before a release tho!)
>>> translated | ||
"Hello, name!" | ||
>>> errs | ||
[FluentReferenceError('Unknown external: name')] |
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.
it's a side note for @stasm mostly, but every time I put a readers hat on I see this as a flaw that we place the id as a raw string. I'd love us to consider { name }
and { $name }
here at least.
fluent/context.py
Outdated
return babel.plural.to_python(self._babel_locale.plural_form) | ||
|
||
@cachedproperty | ||
def _babel_locale(self): |
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.
Why not resolve it in the constructor?
part_count = attr.ib(default=0) | ||
|
||
|
||
def resolve(context, message, args, errors=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.
I think that passing a context
is quite safe here and I saw Stas using that convention around
I do agree that a single dict for all IDs would be useful, but instead of passing a locale, I'd recommend passing a locale fallback chain as provided to the FluentBundle
constructor.
fluent/utils.py
Outdated
@@ -0,0 +1,43 @@ | |||
class cachedproperty(object): |
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.
Is there a value of that over typical lazy override where you define a property as a getter that after initial execution overrides the property with its result?
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.
cachedproperty is quite neat in that it has zero overhead after it has run once (not even a function call, due to the way that Python look things up in object/class dicts), plus it is neat to implement. (Maybe that is what you meant by "overriding the property with its result", not sure, cachedproperty simply wraps this behaviour up neatly).
However, I don't think we really need it at all - the only use for this laziness was not really necessary.
I'm adding @stasm as a reviewer since he said he'd like to look at the resolve before we land this. Stas also wanted to update the directory structure and I'm not sure if it's something we want to do before landing this (and would require rebasing) or after (I'd vote for after ;)). |
I opened #79 to discuss the planned changes to the directory structure. @spookylukey I'd love your input there and I'd also like to coordinate the order of landing. My understanding is that this PR needs to be rebased on top of master anyways. If we do it with the current directory structure, it will also require implementing changes introduced by Syntax 0.8, because that's what the |
@spookylukey Should we close this in favor of #81? |
Yes, I was leaving it open in case there were any more comments on the code, not sure if closing affects that. |
Implementation of #65
This is incomplete, but at a reviewable level - the remaining work to be done shouldn't influence the overall design that much.
I'm unlikely to get back to this soon, but leaving this here so that others are aware that a start has been made, and if anyone wants to give feedback then they can.