Thinking out loud - @nmaloof94

Those of you that have gone through the forum have probably seen @austen’s thread titled Thinking out loud. If you haven’t, you should check it out. It’s kind of like a running blog that he keeps here, on the forum, outlining some of the thoughts he’s having on whatever project he’s currently working on. I operate in a similar way: I find that I tend to overcome obstacles if I “think out loud” and document my thoughts in some way (and, therefore, I’m stealing his idea). It falls in line with the advice Zed gives us, where we should write out our problem and what it is we’re trying to accomplish. Plus, you get to see other people sometimes struggle and overcome certain obstacles in programming, which is reassuring in its own way.

I’m currently working on the very last exercise in Learn Python 3 the Hard Way, where we piece together everything we’ve learned in the last ten or so exercises. I’ve gotten the code working, now I need to start refactoring it and patching up some of the bugs that we’ve implemented. I’m starting at the very bottom of our planisphere module, with our load_room and name_room functions:

def load_room(name):
    """
    There is a potential security problem here.
    Who gets to set name? Can that expose a variable?
    """
    return globals().get(name)

def name_room(room):
    """
    Same possible security problem. Can you trust room?
    What's a better solution than this globals lookup?
    """
    for key, value in globals().items():
        if value == room:
            return key

In short, load_room looks for our room’s name in the global namescape and grabs its vale, which is the object we’re looking to pass when we render our template. name_room returns the key we’re using to help “set up” our session. These parts I understood.

What I didn’t understand was our security issue concerning the globals() lookup. Zed was able to shed some light on this, though:

In this context, we could whitelist our global variables. This way, we control what name is set to, and users can’t go in and hack our script apart (hopefully).

1 Like

I should probably get @austen and you @nmaloof94 to coordinate on a more descriptive name for these blog-threads. Otherwise I’ll get confused trying to track it. Maybe just tack your name onto it? “Thinking out Loud @austen” and “Thinking Out Loud @nmaloof94”?

Differentiating the titles is a good idea. However, I no longer have the option to edit my original post (the one which had the thread title).

@nmaloof94, can you still edit the title for your journal thread? If not, I’m about to start ex37 today. I could just begin a new thread with a totally different title.

Sure, I can edit it! I’ll add my name to my journal title.

1 Like

So, today I spent a little time working on a couple different topics in our web game: our whitelist and our help system.

Here’s what I currently have for our whitelist (@zedshaw, is this what you were talking about?):

def load_room(name):
    whitelist = [
        central_corridor, laser_weapon_armory, the_bridge, escape_pod,
        the_end_winner, the_end_loser, death
    ]

    loaded_room = globals().get(name)

    if loaded_room in whitelist:
        return loaded_room
    else:
        exit(0)

def name_room(room):
    whitelist = [
        central_corridor, laser_weapon_armory, the_bridge, escape_pod,
        the_end_winner, the_end_loser, death
    ]

    if room in whitelist:
        for key, value in globals().items():
            if value == room:
                return key
    else:
        exit(0)

One way I figured I could validate our room and name parameters being passed was to declare a whitelist variable (literally a list of approved objects) in the local namespace of the function. This is more of a quick fix, however, as I’d almost rather do away with accessing objects globally altogether. Instead, I’m considering creating a dictionary object in each function and nesting another dictionary (or dictionaries) inside of the initial dictionary. In the case of our name_room function, I could just use nested for loops to iterate over the itemized key-value mappings. I’ll likely work on this part, as well as move on to another Study Drill question tomorrow.

I also worked on our help system, which will help provide the user some guidance when navigating our map. I also came up with a fairly simple solution for this.

else:
        action = request.form.get('action')

        if action == 'help':
            return redirect(url_for("help"))
        else:
            if room_name and action:
                room = planisphere.load_room(room_name)
                next_room = room.go(action)

                if not next_room:
                    session['room_name'] = planisphere.name_room(room)
                else:
                    session['room_name'] = planisphere.name_room(next_room)

            return redirect(url_for("game"))

All I did here was add an if-else statement to our /game index. If our action object is equal to ‘help’, then we’ll redirect the user to the /help index:

@app.route("/help", methods=['GET', 'POST'])
def help():
    room_name = session.get('room_name')

    if request.method == 'GET':
        room = planisphere.load_room(room_name)
        return render_template('show_help.html', room=room)
    else:
        action = request.form.get('action')

        if room_name and action:
            room = planisphere.load_room(room_name)
            next_room = room.go(action)

            if not next_room:
                session['room_name'] = planisphere.name_room(room)
            else:
                session['room_name'] = planisphere.name_room(next_room)

        return redirect(url_for("game"))

Hardly anything we’re doing here is original; nearly all of the code from our /game index is being recycled. The only thing different is that I’m rendering my show_help.html template.

{% extends "layout.html" %}

{% block content %}

<h1>{{ room.name }}</h1>

<pre>{{ room.help }}</pre>

<p>
<form action="/game" method='POST'>
    - <input type="text" name="action"> <input type="SUBMIT">
</form>
</p>

{% endblock %}

Even our template is virtually identical to our other templates. All of them are inheriting the layout of our layout.html template, and use the same exact form to send user input to the server. I also added an attribute to be initialized with each instance of Room we create: our help attribute.

Much of this code might need to be refactored as I progress deeper into the exercise, especially as I add on to the map and make it more feature-rich. I also need to research defining variables globally and locally and seeing which action will offer me more security, were I to put this game on the internet. Would it be more difficult for someone to tamper with my objects if I created nested dictionaries in the load_room and name_room functions?

Took a very long break from posting here, but I’m resuming that today. I’ve added quite a bit of functionality to my web application, but I don’t entirely feel like going through that right now. I’ll make another post about that towards the end. In short, I’ve added user authentication and put certain checks in place (i.e. only registered users that are logged in can access the game). I still need to improve the layout of my pages, but that’s something I might either do last or get back to at a later date. I’m pretty eager to get started on Learn More Python the Hard Way.

What I have left, as far as I can tell, is implementing a more advanced input processor, adding more rooms to the map, and creating several more maps (though I may just add more rooms and make my game less of a linear progression), as well as some debugging.

I want to be able to handle words that aren’t currently a part of my lexicon by dynamically adding them. My best bet, I’m thinking, is to use a module that is able to access an online dictionary.

PyDictionary seems pretty cool. From the PyDictionary module you import the PyDictionary class. Then, you just create an instance of the PyDictionary class to access all of its constants and methods:

dictionary = PyDictionary()

Here’s an example of one of its methods, the meaning method:

dictionary.meaning("python")

{'Noun': ['large Old World boas', 'a soothsaying spirit or a person who is possessed by such a spirit', '(Greek mythology']}

It uses the word’s class as its key, which I could use to set the word’s type in my lexicon. I was thinking of doing something like this: ’

dictionary = PyDictionary()
word_class = dictionary.meaning(word).keys()

for key in word_class:
    lexicon[word] = word_class

Some of the words players might use, however, might have multiple word classes, so I need to have something in place to handle that and set the right word class as our value. I’m thinking I could probably use the peek function from our parser module, the implement some control flow to make sure the word is set to the right value (although, I want to try and see if there’s a better way to do this than if-else statements).

I also need to make sure I actually want to use this module I found online. It takes a long time for the word’s dictionary to print. This would mean adding words and their word classes to my lexicon would take a long time, which, were I to actually make a game like this and put it online, would get very annoying very quickly.

I saw a couple other modules that were fairly similar in their scope. I may check them out.