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

[WIP] Initial work refactoring docs/ examples/ class Meta to class arguments, implementing explicit imports. #992

Open
wants to merge 1 commit into
base: next/master
Choose a base branch
from

Conversation

changeling
Copy link

Refactoring firehose imports to explicit imports in docs/, examples/.
Some blackening of example code in docs/.
Refactoring of class Meta into class arguments.

This is a first pass of docs/ and examples/ using explicit imports and refactoring class Meta into class arguments.

Very little thought towards functionality of the documentation examples, just standardizing.

@changeling changeling force-pushed the refactor-examples-docs-for-python-3.0 branch 2 times, most recently from 57bd959 to 9472e89 Compare June 4, 2019 08:21
@dvndrsn
Copy link
Contributor

dvndrsn commented Jun 4, 2019

Hm.. This is gonna conflict quite a bit with my docs PR.. we need to start merging these faster.. 🤔

Copy link
Contributor

@dvndrsn dvndrsn left a comment

Choose a reason for hiding this comment

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

Looks pretty good. Some questions and comments for discussion.

We should consider putting an item on docs backlog for adding doctest to the pipeline to ensure the examples are valid.

@@ -1,34 +1,27 @@
import graphene
from graphene import ObjectType, Schema, Field, String
from graphene import relay
Copy link
Contributor

Choose a reason for hiding this comment

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

Why keep this as a module import rather than importing relay classes from graphene as well (all are available in the top level module) or import the classes from graphene.relay?

Copy link
Author

Choose a reason for hiding this comment

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

I wanted to preserve this as a reminder to revisit this in conversation. Do we want to go completely explicit, or just top-level to highlight the sub-package. I'm waffling back and forth in my head on this.

class UploadFile(ClientIDMutation):
class Input:
pass
# nothing needed for uploading file
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment is unrelated to the changes that you did:

It feels weird to have example code with commented out bits. I'd prefer to have a viable example which uses comments to highlight the important things.

Copy link
Author

Choose a reason for hiding this comment

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

That's a good conversation to have. We need to start a doc/channel to discuss these things. One thought: Any code we use in the docs that mirrors actual code in the examples, IMHO, ought to be taken as is from the example code. What do you think?

Copy link
Author

Choose a reason for hiding this comment

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

Made a note in my local TODO of this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I really like this idea to extract most examples from a working API. I had a few ideas that I brainstormed in this issue:

#970

class Input:
ship_name = graphene.String(required=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I like this better without graphene graphene graphene everywhere!

Did we have a discussion with folks to confirm that everyone is generally on the same page? The current convention is followed in all kinds of places so it will take some doing to change all the code examples.

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 all for this. It makes the code much easier to read.

Copy link
Author

Choose a reason for hiding this comment

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

@dvndrsn We had a brief discussion on Slack about this. One reason for this PR as WIP is to further the conversation. See: https://graphenetools.slack.com/archives/CGR4VCHUL/p1559524058000700

@jkimbo I feel the same way.

@@ -12,7 +12,7 @@ Let’s build a basic GraphQL schema from scratch.
Requirements
------------

- Python (2.7, 3.4, 3.5, 3.6, pypy)
- Python (3.6+, pypy)
Copy link
Contributor

Choose a reason for hiding this comment

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

3.5 should be supported too (until 3.8 is released in October).

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we might want to clarify the versions supported and the plan in ROADMAP.md

Copy link
Author

Choose a reason for hiding this comment

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

I went with 3.6+ after looking at what we're testing for in CI. This is absolutely open to discussion!

'''A ship in the Star Wars saga'''
class Meta:
interfaces = (relay.Node, )
class Ship(ObjectType, interfaces=(relay.Node,)):
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you prefer this way of supplying meta args? I've not used it much myself since I'm used to Meta inner class with Django.

Copy link
Member

Choose a reason for hiding this comment

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

I've not used this way before either but it's quite nice. I guess since we're dropping Python 2 support we should just go all out on Python 3 features.

Copy link
Author

Choose a reason for hiding this comment

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

@dvndrsn I like this for graphene, as it feels more pythonic, with the Meta perhaps having it's place in graphene-django.

It's offered in UPGRADE-v2.0.md under Meta as Class arguments, which may highlight it as best practice to new users/upgrading folks.

Also, class Meta seems to offer some confusion among users, if SO is any indication:

https://stackoverflow.com/search?q=%5Bgraphene-python%5D+class+Meta

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that's true. Especially with Graphene-Django and Graphene-SQLAlchemy. Both of those projects have a lot of use of Meta parameters.

Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like docs should at least present both approaches. As I tend to use Meta anyway in my Django project, where graphene-django is also used, it will be nice to have a clear indication that you can do that with graphene also. That may not be clear for folks less familiar with Python.

docs/quickstart.rst Outdated Show resolved Hide resolved
docs/types/objecttypes.rst Show resolved Hide resolved
ok = Boolean()
person = Field(lambda: Person)

def mutate(self, info, name):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def mutate(self, info, name):
def mutate(root, info, name):

Copy link
Author

Choose a reason for hiding this comment

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

The no-code done yet, but another good catch.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the topic of what is the name of the first argument of a resolver called is its own docs related discussion/issue. Its definitely not self, so this is a good change IMO.

Copy link
Author

Choose a reason for hiding this comment

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

Somewhere in the ecosystem, in an Issue, there's a great discussion regarding root as best, but for the life of me I can't find it again. If I do, I'll pop it into the docs tracking issue.

best_friend = graphene.Field(Person)
class Query(ObjectType):
me = Field(Person)
best_friend = Field(Person)

def resolve_me(_, info):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def resolve_me(_, info):
def resolve_me(root, info):

Copy link
Member

Choose a reason for hiding this comment

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

Not sure why some of these suggestions have removed a leading space, that wasn't what I was suggesting...

Copy link
Author

Choose a reason for hiding this comment

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

Ha. No worries. Even if I missed that, I'm trying to blacken the example code, so that would be fixed anyhow, I think.

ok = graphene.Boolean()
person = graphene.Field(lambda: Person)
ok = Boolean()
person = Field(lambda: Person)

def mutate(self, info, name):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def mutate(self, info, name):
def mutate(root, info, name):

Copy link
Author

Choose a reason for hiding this comment

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

This is suggesting the root change? Good catch, will fix on second pass.

Copy link
Contributor

Choose a reason for hiding this comment

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

I made a bunch of changes related to root argument in this PR as well: #969

Please nitpick, comment, whatever all you want. That PR and this one are going to conflict in ways that will be annoying to resolve, I'm sure.

Copy link
Author

Choose a reason for hiding this comment

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

Just a thought, but perhaps we ahould look at the two, and consider which will make the most likely to conflict changes and stage them in order to minimize the conflicts? That is, if one makes minor syntactical changes and the other makes bigger changes to actual layout and wording...?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we we can resolve the conflicts easily enough, but maybe if we can groom some stories around documentation there'll be less overlap.

Copy link
Contributor

@radekwlsk radekwlsk Jul 21, 2020

Choose a reason for hiding this comment

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

I think that every method where root or parent is used as the first argument it should be decorated with @staticmethod. Especially if that argument is not an instance of the class. Current approach is not pythonic.

human_by_name = graphene.Field(Human, name=graphene.String(required=True))

class Query(ObjectType):
human_by_name = Field(Human, name=String(required=True))

def resolve_human_by_name(_, info, name):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def resolve_human_by_name(_, info, name):
def resolve_human_by_name(root, info, name):

Copy link
Author

Choose a reason for hiding this comment

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

See previous comments. Got it.

docs/types/objecttypes.rst Show resolved Hide resolved
'''A ship in the Star Wars saga'''
class Meta:
interfaces = (relay.Node, )
class Ship(ObjectType, interfaces=(relay.Node,)):
Copy link
Member

Choose a reason for hiding this comment

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

I've not used this way before either but it's quite nice. I guess since we're dropping Python 2 support we should just go all out on Python 3 features.

class Input:
ship_name = graphene.String(required=True)
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 all for this. It makes the code much easier to read.

@jkimbo
Copy link
Member

jkimbo commented Jun 4, 2019

Oh just saw this is WIP sorry @changeling . Feel free to ignore my comments and I can give it a proper review when the PR is actually ready.

@changeling
Copy link
Author

changeling commented Jun 4, 2019

Oh just saw this is WIP sorry @changeling . Feel free to ignore my comments and I can give it a proper review when the PR is actually ready.

@jkimbo Not at all! That's exactly why I made this PR/WIP. To get some more eyes on. Thanks for the feedback and review.

@changeling changeling requested a review from phalt as a code owner June 4, 2019 22:32
@@ -1,21 +1,24 @@
from graphene import InputObjectType, Float, ObjectType, String, Field, Schema

# Naming confusion/conflict with class Mutation and explicit import of graphene.mutation.
Copy link
Author

Choose a reason for hiding this comment

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

I'd love some thoughts on this code. We're importing Mutation from graphene, previously used as graphene.Mutation, and declaring class Mutation.

Side note: We need to do a run through of all the doc examples in a functional context.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that's no good. This example will need to be improved.

Copy link
Contributor

Choose a reason for hiding this comment

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

And that is exactly why you should just import graphene so each external dependency is always clearly distinguishable as external.

@changeling changeling force-pushed the refactor-examples-docs-for-python-3.0 branch from 07b3e47 to 65d799f Compare June 4, 2019 23:03
@dvndrsn dvndrsn added this to To do in Docs and examples Jun 4, 2019
@dvndrsn dvndrsn moved this from To do to In progress in Docs and examples Jun 4, 2019
@changeling changeling force-pushed the refactor-examples-docs-for-python-3.0 branch from 65d799f to ff988a6 Compare June 5, 2019 00:11
Initial work refactoring class Meta to class arguments.

Refactoring firehose imports to explicit imports.
More blackening of example code.
More refactoring of `class Meta` into class arguments.
@changeling changeling force-pushed the refactor-examples-docs-for-python-3.0 branch from ff988a6 to eefcf40 Compare June 5, 2019 00:17
Copy link

@phalt phalt left a comment

Choose a reason for hiding this comment

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

Some comments around my usage of docs.

@@ -40,22 +41,23 @@ You can pass variables to a query via ``variables``.

.. code:: python

class Query(graphene.ObjectType):
user = graphene.Field(User, id=graphene.ID(required=True))
class Query(ObjectType):
Copy link

Choose a reason for hiding this comment

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

Whenever I read docs examples like this I always ask "where the hell do I import that from though?".

I feel like if we remove the graphene bit we need to show people where it is imported from. Because copy/pasting this won't work.

Copy link
Author

Choose a reason for hiding this comment

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

This is something I've been meaning to bring up. I think we should, wherever possible, make snippets in the docs complete. That is, again as much as possible, allow them to be copy/pasted into a .py file and actually work. I echo your thought here.

Copy link
Member

Choose a reason for hiding this comment

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

+1 on including imports. I always get confused with that too.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also importing a package would be a better approach rather than import each class separately. Makes reading code much easier if each external reference is easily identifiable.

@@ -35,15 +35,17 @@ one field: ``hello`` and an input name. And when we query it, it should return `

.. code:: python

import graphene
from graphene import ObjectType, Schema, String
Copy link

Choose a reason for hiding this comment

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

This is what I mean about adding imports in docs - we should be doing it consistently.

Copy link
Author

Choose a reason for hiding this comment

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

Agreed. See above.

Copy link
Author

Choose a reason for hiding this comment

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

Is there a sphinx or other tool for actually embedding live code from, say, examples/, for inclusion inline in the docs?

Copy link
Contributor

@dvndrsn dvndrsn Jun 9, 2019

Choose a reason for hiding this comment

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

This might be what you're looking for @changeling literalinclude:

https://quick-sphinx-tutorial.readthedocs.io/en/latest/code.html

This option emphasize-lines is interesting too. Both are built into sphinx

@maquino1985
Copy link

maquino1985 commented Jun 9, 2019

Comment on the use of graphene. graphene. graphene. everywhere. At first I thought this was annoying and didn’t understand why it was used in the docs but I’ve got back to importing graphene types this way because they have the same name as typing types, and we try to type everything we can as it is the way python is (thankfully) headed.

On that note, is there a reason that this library doesn’t use the typing module? It would at least help new users like myself to figure out how to get things to work until this project starts moving more quickly on its various issues.

For example root, context, info — what are these things? What are the possible values or uses for them? Sans proper documentation, at least knowing what types the authors intended those arguments to accept helps tremendously.

@@ -32,17 +33,16 @@ needs to have the ``description`` property on it.

.. code:: python

class Episode(graphene.Enum):
class Episode(Enum):
Copy link
Contributor

Choose a reason for hiding this comment

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

Definitely need an import in this example for disambiguation.

Suggested change
class Episode(Enum):
from graphene import Enum
class Episode(Enum):

@stale
Copy link

stale bot commented Aug 8, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Aug 8, 2019
@jkimbo
Copy link
Member

jkimbo commented Aug 9, 2019

@changeling were you planning on updating this PR?

@stale stale bot removed the wontfix label Aug 9, 2019
@stale
Copy link

stale bot commented Oct 8, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Oct 8, 2019
@stale stale bot closed this Oct 15, 2019
@jkimbo jkimbo reopened this Oct 18, 2019
@stale stale bot removed the wontfix label Oct 18, 2019
@stale
Copy link

stale bot commented Jan 16, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jan 16, 2020
@jkimbo jkimbo removed the wontfix label Jan 20, 2020
@nikitowsky
Copy link

@changeling what state on 3.0?

@@ -52,16 +52,16 @@ the ``Enum.from_enum`` function.

.. code:: python

graphene.Enum.from_enum(AlreadyExistingPyEnum)
Enum.from_enum(AlreadyExistingPyEnum)
Copy link
Contributor

Choose a reason for hiding this comment

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

Like mentioned in https://github.com/graphql-python/graphene/pull/992/files#r291840336 each use of class that also exists in standard lib should have import. Best if all examples have all imports required. I personally would keep import graphene and graphene.xxx as that is a good practice to import packages rather than members.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Docs and examples
  
In progress
Development

Successfully merging this pull request may close these issues.

None yet

7 participants