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

Request config #2949

Merged
merged 16 commits into from
Apr 25, 2018
Merged

Request config #2949

merged 16 commits into from
Apr 25, 2018

Conversation

asvetlov
Copy link
Member

What do these changes do?

Add request.config and request.config_dict properties.
Both props do lookup into parent app if the name is not found in sub-application.
.config is a namespace (req.config.key) but .config_dict is a mapping (req.config_dict['key']).

Both structures are immutable, to setup config value use app['key'] = val on application initialization stage.

Are there changes in behavior for the user?

The change is backward compatible but two new attributes are added to request instance.

Related issue number

#2689

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
    • The format is <Name> <Surname>.
    • Please keep alphabetical order, the file is sorted by names.
  • Add a new news fragment into the CHANGES folder
    • name it <issue_id>.<type> for example (588.bugfix)
    • if you don't have an issue_id change it to the pr id after creating the pr
    • ensure type is one of the following:
      • .feature: Signifying a new feature.
      • .bugfix: Signifying a bug fix.
      • .doc: Signifying a documentation improvement.
      • .removal: Signifying a deprecation or removal of public API.
      • .misc: A ticket has been closed, but it is not of interest to users.
    • Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files."

for mapping in reversed(self._maps):
# reuses stored hash values if possible
d.update(mapping)
return iter(d)
Copy link
Member

Choose a reason for hiding this comment

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

It feels like you can have there the same iter(set().union(*self._maps)) instead of another dict construction.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did copy collections.ChainMap without careful thinking :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's keep it for sake of shared implementation with ChainMap

Copy link
Member Author

Choose a reason for hiding this comment

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

Iteration is not the most common call, get/__getitem__ doesn't create a dict.
Moreover your proposal constructs a set, not sure what is better.

@@ -741,3 +742,63 @@ def set_result(fut, result):
def set_exception(fut, exc):
if not fut.done():
fut.set_exception(exc)


class ChainedProps(Mapping):
Copy link
Member

Choose a reason for hiding this comment

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

I think the ChainedMaps would be better name here since it's a mapping. Props suffix leaves the feeling that it's something about attr-based access. While ChainedMaps causes collision with stdlib ChainMap, still, they have a bit different naming what should cause suspicions. However, if we make this class for internal-only usage, than own ChainMap is fine.

Copy link
Member Author

Choose a reason for hiding this comment

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

Naming is the hardest thing!
Maybe we can invite a name without chain word?

Copy link
Member

Choose a reason for hiding this comment

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

Hm...How about to just a Mapping? With the trick that all the maps will get merged right at the moment of it creation?

Copy link
Member

Choose a reason for hiding this comment

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

I mean, if we do merge all the maps, than this class has no reason to exists. The merge result could be returned as MappingProxy - well known immutable mapping.

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's name it ChainMapProxy and keep existing implementation -- I don't like early merging.


def __repr__(self):
content = ", ".join(map(repr, self._maps))
return 'ChainedProps({})'.format(content)
Copy link
Member

Choose a reason for hiding this comment

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

Here we should reuse class name in repr to avoid any confusions with subclasses. I know, there wouldn't any subclasses of those, but still this little bit could save few debugging hours for some hacker.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't want to allow subclassing. Hmm, should do it explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done


@property
def config(self):
return Namespace(self.config_dict)
Copy link
Member

Choose a reason for hiding this comment

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

I'm in a very doubt if we need not a mapping class here. This will cause two problems:

  1. Inconsistent access to config values. While names are valid Python attributes it's all fine, but how to access to a value with mycomponent.cache? Here, user have to fallback to getattr(request.config, 'mycomponent.cache').
  2. Linters. Linters will complain about accessing to unknown class attributes. This will be very annoying.

I would prefer to have one way to work with the config that works and right. So far, config_dict is more powerful and useful interface to it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I heard complains many times that IDE doesn't autocomplete string keys but does it for custom attributes.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, they don't do keys autocomplete unless you explicitly define those keys in the same scope. However, their ability to autocomplete custom attributes is limited and based on type knowledge and similar usages.

However, for pylint you will have to put Namespace class into ignored-classes since it will think that it's your fault since you're referencing to unknown class instance attribute.

Copy link
Member Author

Choose a reason for hiding this comment

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

@socketpair I recall you wanted the feature.
Please explain your opinion

Copy link
Member Author

Choose a reason for hiding this comment

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

@asvetlov
Copy link
Member Author

Silence.
@kxepal I suggest dropping request.config but keeping request.config_dict.
Let's reserve .config for future usage (I still doubt about the best API).

@kxepal
Copy link
Member

kxepal commented Apr 25, 2018

@asvetlov
Ok, sounds good.

@asvetlov
Copy link
Member Author

Done

@codecov-io
Copy link

codecov-io commented Apr 25, 2018

Codecov Report

Merging #2949 into master will increase coverage by 0.09%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2949      +/-   ##
==========================================
+ Coverage   97.99%   98.08%   +0.09%     
==========================================
  Files          40       40              
  Lines        7523     7622      +99     
  Branches     1318     1350      +32     
==========================================
+ Hits         7372     7476     +104     
+ Misses         48       47       -1     
+ Partials      103       99       -4
Impacted Files Coverage Δ
aiohttp/web_request.py 99.7% <100%> (ø) ⬆️
aiohttp/helpers.py 97.05% <100%> (+0.21%) ⬆️
aiohttp/client_reqrep.py 98.37% <0%> (+1.09%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e7c1119...87f1fe4. Read the comment docs.

Copy link
Member

@kxepal kxepal left a comment

Choose a reason for hiding this comment

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

LGFM, nice feature!

@asvetlov asvetlov merged commit fb0106a into master Apr 25, 2018
thehesiod pushed a commit to thehesiod-forks/aiohttp that referenced this pull request Apr 26, 2018
@asvetlov asvetlov deleted the request-config branch September 7, 2019 21:49
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.

None yet

3 participants