
On Thu, Apr 23, 2020 at 6:18 AM M.-A. Lemburg <mal@egenix.com> wrote:
On 21.04.2020 15:51, Chris Angelico wrote:
On Tue, Apr 21, 2020 at 7:01 PM M.-A. Lemburg <mal@egenix.com> wrote:
Instead of keeping values in local variables, you store them in the namespace object and because this knows about its attributes you can do a lot more in terms of introspection than what is possible when just relying on local variables in a function call context.
See e.g. argparse's various namespace uses for an example in the stdlib.
Let me make things even clearer here. This is the entire code for the index page handler.
@app.route("/") @app.route("/editor/<channelid>") def mainpage(channelid=None): # NOTE: If we've *reduced* the required scopes, this will still force a re-login. # However, it'll be an easy login, as Twitch will recognize the existing auth. if "twitch_token" not in session or session.get("twitch_auth_scopes") != REQUIRED_SCOPES: return render_template("login.html") user = session["twitch_user"] if channelid is None: channelid = user["_id"] try: channelid = str(int(channelid)) except ValueError: # If you go to /editor/somename, redirect to /editor/equivalent-id # Bookmarking the version with the ID will be slightly faster, but # streamers will usually want to share the version with the name. users = query("helix/users", token=None, params={"login": channelid})["data"] # users is either an empty list (bad login) or a list of one. if not users: return redirect("/") return redirect("/editor/" + users[0]["id"]) if not may_edit_channel(user["_id"], channelid): return redirect(url_for("mainpage")) database.create_user(channelid) # Just in case, make sure the database has the basic structure channel = get_channel_setup(channelid) sched_tz, schedule, sched_tweet = database.get_schedule(channelid) if "twitter_oauth" in session: auth = session["twitter_oauth"] username = auth["screen_name"] twitter = "Twitter connected: " + username tweets = list_scheduled_tweets(auth["oauth_token"], auth["oauth_token_secret"], sched_tz) else: twitter = Markup("""<div id="login-twitter"><a href="/login-twitter"><img src="/static/Twitter_Social_Icon_Square_Color.svg" alt="Twitter logo"><div>Connect with Twitter</div></a></div>""") tweets = [] error = session.get("last_error_message", "") session["last_error_message"] = "" return render_template("index.html", twitter=twitter, username=user["display_name"], channel=channel, channelid=channelid, error=error, setups=database.list_setups(channelid), sched_tz=sched_tz, schedule=schedule, sched_tweet=sched_tweet, checklist=database.get_checklist(channelid), timers=database.list_timers(channelid), tweets=tweets, )
Now, show me which part of this should become a "namespace object". That namespace would be *unique* to this function - it would have no purpose or value outside of this one exact function. Show me how the use of such a namespace object would improve this code. There is no way that you would be able to share it with any other function in the entire program.
Instead of saving the variables in the local scope, you'd create a namespace object and write them directly into that object, e.g.
ns = Namespace() ns.auth = session["twitter_oauth"] ... ns.tweets = [] return render_template("index.html", ns)
Another advantage of this style is that debugging becomes much easier, since you can see the full context used by the rendering function and even provide custom output methods for the namespace content. Your local scope doesn't get cluttered up with all the namespace details.
Meaning that every *local* use of those same names now has to be adorned with "ns." to access the same thing: ns.channel = get_channel_setup(ns.channelid) Thanks, I think that's even worse than using locals(). Didn't think that was possible. :) ChrisA