HNHacker News
TopNewBestAskShowJobs

irahul

3,322 karma · joined February 9, 2010

rahul AT thoughtnirvana DOT com
submissionscomments
irahul··on Building Large Flask Apps in the Real World
Haven't used flask in some time now, but most of your objections are missing the mark.

> GLOBALS: Flask has a lot of globals (from flask import g, request, current_user, etc.) A better web handler function looks like this handle(request) -> response. Any other web framework that I know does it like this.

Flask's request is not global. I have had this discussion with someone else before:

https://news.ycombinator.com/item?id=6662623

FWIW, flask's "from flask import request" never had been a blocker for me, whereas django's injected request variable had made me write middleware to preseve requests as thread locals so that I can access it elsewhere. Werkzeug does the same thing, except it does it more robustly.

> MIDDLEWARE: Flask promotes writing middleware as decorators functions, while it looks nice, it not really useable anywhere else, and its not really a standard. At least you can still use WSGI middleware.

I don't see an issue. If you can solve it using simple python(decorators), solve it using Python.

> BLUEPRINT are nice, but have issues: When you create a blueprint, for a sub app, you cant set error handlers. They only work on app level. Also (small issue) You cant add blueprint specific middleware.

Rather than calling app.before_request, you can call blueprint.before_request and it will work only for that blueprint.

> SITEMAP @route has disadvantages For big apps, its nice to have a single routes.py file where you can see all urls the app supports, and which methods

Then don't use the decorator. Have a routes.py and call app.add_url_rule

I have had a template with all of this from some time back

https://github.com/rahulkmr/flask-bigapp-template

irahul··on After selling his company to Google, this man now wants to block ad-blockers
> publishers have the right to respond, either by blocking access to ad-block users

All the best with that. The number of sites on which I will have to disable ad-blocker because you won't show the content otherwise is in single digits.

irahul··on Why I can’t stop using Vim
> it genuinely interests me how Vi users put up with the constant need to strain their pinky to switch to command mode.

Is it any different from how emacs users put up with Ctrl/Alt?

You aren't looking to switch, but if anybody is, install this https://github.com/tpope/vim-rsi for basic keybinding in insert mode. This is quite a short file. If you want, you can simply copy it to your .vimrc. For non-basic movement/editing, switch to normal mode and edit.

irahul··on Why I can’t stop using Vim
What problems are you facing? Setting up evil mode takes just what the emacs wiki says.

1. Install emacs. Start emacs. "Alt-x package-install" press enter "evil" press enter.

2. Open your .emacs or .emacs.d/init.el.

  (require 'evil)
  (evil-mode 1)
Done.
irahul··on Why I can’t stop using Vim
What is there to learn about the terminal? You learn the shell, coreutils, shell scripting, sed, awk, editor...Learning these isn't a measure of competency. If you just want name dropping, John Carmack works in Visual Studio, Notch works in Eclipse.
irahul··on Why I can’t stop using Vim
> 5. :map <M-f12> @z to execute buffer z 6. Hit Alt-f12 over and over until I'm done

Why map? @z and @@ till your are done? Or if you have this in your vimrc:

      vnoremap @@ :normal! @@<CR>
@z, visual select, and @@.
irahul··on Why I can’t stop using Vim
> Remember, kids! ":set paste".

Or get used to ctrl-[ which is easier to type than Esc, doesn't skip events(like ctrl-c), and doesn't interfere with anything.

irahul··on Why I can’t stop using Vim
I am not sure which version of vim you are on. I have been using clibpoard paste for a long time in Ubunut. Do a ":version" in vim and check if it's compiled with +clipboard. If it is, then it should work.

Also, you can have this in your vimrc:

  set clipboard=unnamedplus
And then your vim copy-paste will be in sync with system clipboard(no need for +p or +y).
irahul··on Python 3 can revive Python
> If we could just have tail call optimization, I think we could make the rest work

Manually thunk/trampoline it?

    class trampoline(object):
        def __init__(self, fn):
            self.fn = fn

        def __call__(self, *args, **kwargs):
            ret = self.fn(*args, **kwargs)
            while isinstance(ret, thunk):
                ret = ret()
            return ret


    class thunk(object):
        def __init__(self, fn, *args, **kwargs):
            self.__dict__.update(fn=fn, args=args, kwargs=kwargs)

        def __call__(self):
            if isinstance(self.fn, trampoline):
                return self.fn.fn(*self.args, **self.kwargs)
            else:
                return self.fn(*self.args, **self.kwargs)


    @trampoline
    def fact(n, accum=1):
        if n <= 1:
            return accum
        else:
            return thunk(fact, n-1, n*accum)

    print fact(1000)


    @trampoline
    def is_even(n):
        if n == 0:
            return True
        else:
            return thunk(is_odd, n - 1)


    @trampoline
    def is_odd(n):
        if n == 0:
            return False
        else:
            return thunk(is_even, n - 1)

    print is_even(1000001)


You only have to write thunk/trampoline utility once, and it is similar to how we do it in clojure.
irahul··on Ack is a grep-like tool, optimised for programmers
> I've never needed to use the rackup "rack" command directly, fortunately, if you do you ought to use a different alias

Or escape alias \rack

irahul··on Show HN: A dead simple static site generator – just two template tags
> I will most likely soon build my own static site generator, because none of the ones out there do exactly what I want.

Why bother with the templating? Use Jinja2?

irahul··on An open question (rant) about Node.js
Idiomatic go concurrency uses channels for synchronization and passing data around. You shouldn't be writing to shared data from multiple co-routines.
irahul··on Emacs is Sexy
There isn't much to a good looking editor. For me, it's choose a decent font and a color scheme and I am done.

Emacs: http://i.imgur.com/NBZJSd6.png

Vim: http://i.imgur.com/LnnVUQe.png

irahul··on Writing OOP using OOP
> Or, were you demonstrating the method overriding + calling to parent use case?

Yep. This gist is what I run co-workers through for demonstrating OOP in JS.

irahul··on Writing OOP using OOP
I prefer to keep things simple.

https://gist.github.com/rahulkmr/9482010

It makes working with teams easier.

irahul··on A Vim Tutorial and Primer

    inoremap <C-x>c <esc>bgUWea
Type the identifier in lower case, and then <C-x>c(or whatever you feel like mapping it to).
irahul··on Django plugins you shouldn't start without
> There's also poor testability, inability to re-use functions that reference the request internally, inability to statically analyze dependencies (less of an issue in Python, since the lack of types makes it hard to analyze them anyway), additional context that the developer must keep in his head, and inability to cross-reference parameters & call-sites and identify where a given variable is coming from in code browsers or debuggers.

I don't know. Most of them seem like a non issue to me. Shouldn't the api cover that testability(of request)? At least, it does in Flask. Re-using a function which uses request won't be any different from re-using a function which uses datetime. I do get your point about difficulty of analyzing Python owing to dynamic typing and monkey patching, but I couldn't connect it to context local. As for cross referencing, you just treat request as you treat datetime.

> Singletons are no better - if you dress it up as RequestManager.get_current_request(), it's just as bad. If you don't understand why something is a bad idea, complying with the letter of the law without complying with the spirit doesn't really get you much.

I won't do that. That is just a tongue in cheek comment to appease "death to globals" police.

> Now I want my programs to do as little as possible and have access to as little as possible.

I don't disagree with that but assuming that the request object is available for the request lifecycle is pretty reasonable.

> Usually I end up pulling out the values I want inside the view function and calling any helpers using only those values, so I don't pass the request around anyway.

You don't pass around request in a general workflow(why would you?). But consider this https://github.com/tschellenbach/Django-facebook. This does a post save signal when a new user registers. I need to send a welcome mail to the user asking him to activate his account. I need request.build_absolute_uri to construct the uri, and guess what, I don't have request in the callback and I don't have a way to obtain it. Now there can be other ways I can solve it - configure sites and generate url using sites; simply have the base url in settings and construct the url by doing a urljoin; may be in some alternate universe, django's reverse generates external url. But the thing is, I have needed access to request object on more than a few occasions. Granted if the callback passed me the request object, it won't have been an issue but it doesn't and I had to resort to thread local middleware to save the request object.

> A fourth was that I worked a bunch with both systems and found that adding a parameter to each function along a call path is easy

The issue is apis which you haven't wrote. I can always change the packages myself, but I don't want to sync up with upstream for something I see as a small change.

irahul··on Django plugins you shouldn't start without
> No more so than werkzeug since it works the same way.

I don't know if you are being intentionally dense. Werkzeug doesn't work the same way. Thread locals restrict themselves to thread lifecycle; werkzeug locals restrict themselves to request lifecycle. They are 2 very different things.

> Which is exactly how Python's threadlocals work.

Except for different lifecycle.

> You do realize that nothing stops you from "cleaning up" your threadlocal "when the request completes" right?

That "cleaning up" makes it request local, not thread local. They are global for the duration of the request and one request scope is separate from another request which makes them request local(how it is done is immaterial). Spare me the definition of global. There is no point in continuing this discussion any further since I don't seem to have any epiphany from your insights; and doesn't seem like you are going to have one either.

irahul··on Django plugins you shouldn't start without
> The request object is a threadlocal (or, if greenlet is installed, a greenlet-local).

request objects aren't stored in python's thread local storage. If they were, a thread re-use will leak context local. Werkzeug uses the thread/greenlet id as key in its storage and cleans up when the request completes.

> Which is exactly the same as a global variable in a single-threaded program.

In a single threaded server, the context locals won't leak from one request to another. If they were global, they would. I don't see how request object has anything to do with global variables.

irahul··on Django plugins you shouldn't start without
> So you're saying that if I don't use multithreading my global variables are not, in fact, global?

The request object is context local. If there are multiple concurrent requests, all request have different request objects. I don't know what definition of global variable you have, but this is not it.

irahul··on Django plugins you shouldn't start without
> "In almost every case where we allowed non-local effects, it has bitten us. Badly."

But the request object in flask is not global. If the guarantees break(request locals leak to other requests), it will be a problem. I never have had a request local leak in flask. On the other hand, I have ran into cases where I need the request object in django signals. A simple task of getting the full uri requires either a request object(I would say that's bad api but that's how it is in django) or configuring sites. Bad api or not, if I am in a request, I should have access to it, and Django makes it hard. Dress it up if you want to comply with "no globals" - RequestManager.get_current_request(). Though I don't see how that's any better than `from flask import request` when you know it does the equivalent RequestManager.get_current_request() internally.

irahul··on Django plugins you shouldn't start without
> Views are functions from Request to Response in Django,

The thing that irritates me about django is request is passed as an argument, and if you need if further down the chain, you have to pass it around. I want the request object to be available throughout the request cycle without it being explicitly passed. I like the flask model of importing request when you need it. In Django, I can have a middleware to handle it for me:

    from threading import local
    _locals = local()
    class ThreadLocalMiddleware(object):
        def process_request(self, request):
            _locals.request = request

        @classmethod
        def request(cls):
            return _locals.request

        @classmethod
        def locals(cls):
            return _locals
But then there are edge cases. What if the threads are being reused? What about greenlets? I am thinking of taking out the flask/werkzeug implementation of locals and LocalManager

> Very full-featured templates & helper functions.

Django templates are nice; Jinja2 is nicer:-)

> Lighter-weight microframeworks like Flask don't have this property.

I think it's not just about Flask being light weight. It's more a function of Django existing for longer and being more popular. Granted that Django itself includes lot of batteries which Flask doesn't, but the "somebody has already solved it" is mostly either an extension, or a forum/stackoverflow solution.

irahul··on Time to hand over the reins before Capistrano costs me my youth?
Not compiling when assets haven't changed is a problem. I have been experimenting with https://github.com/ndbroadbent/turbo-sprockets-rails3 - seems to work so far.

For me, the more important thing was not compiling on a production box. Compiling on production box does lots of io and uses way too much cpu. I am ok with a slight delay in deploy due to assets compilation happening on my local machine.

I see a lot of people in this thread criticizing the assets pipeline. But I think it's a good idea with an implementation which is yet to mature. For now, it needs some work on my part(do I check in the assets, do I compile on production box, how do I ensure assets are only compiled when changed etc etc), but overall I am quite happy with the way it works.

irahul··on Time to hand over the reins before Capistrano costs me my youth?
I would actually worry about LDPATH first before trying lxc.
irahul··on Time to hand over the reins before Capistrano costs me my youth?
Yes. Different branch and uncommitted changes are an issue. Different branch can be solved by switching the branch if `git rev-parse HEAD != git rev-parse branch`. Uncommitted changes can be handled by `[[ -n $(git status -s)]]`(simply abort).
irahul··on Time to hand over the reins before Capistrano costs me my youth?
No it doesn't. But throwing away RVM doesn't fix the things RVM fixes. RVM doesn't solve all problems; that doesn't mean it isn't useful for problems it solves.
irahul··on Time to hand over the reins before Capistrano costs me my youth?
> Addressed: https://news.ycombinator.com/item?id=6505227

False dichotomy. There is no technical debt. Both Ruby 1.9.3 and Ruby 2 are functional and used in production. It will take some time to totally move to 2, and when that happens, there will be applications on different release versions of 2. I don't want to wait for os packaging to use a new ruby release, and I don't want to be forced by the packaging to use a particular version(older or newer).

> I don't work in a Ruby shop anymore, but I'd look at Bundler

Um. I use Bundler along with rvm.

> maintaining multiple gemsets on the same machine is an antipattern that points to technical debt.

Having a restriction of not more than one app on one machine is as stupid as it gets.

irahul··on Time to hand over the reins before Capistrano costs me my youth?
I don't see any point in conversing any further since you are being intentionally obtuse. apt-get doesn't help me with different projects using different ruby interpreters and isolated gemsets(and it shouldn't); rvm does. I deploy multiple applications to the same production box, rvm solves multiple interpreters and gemsets, I use it. Your diatribe about package management and rvm is responding to imaginary arguments. Also, package management doesn't keep up with Ruby releases and I would rather install from rvm then packaging my own and having a separate apt server. And as pointed out many times, it's not just about the interpreters but isolating gems.
irahul··on Time to hand over the reins before Capistrano costs me my youth?
Yes, but that won't solve when I have 2 applications deployed on the same production box. rvm solves it.
irahul··on Time to hand over the reins before Capistrano costs me my youth?
Do you deploy with Vagrant? I think container might be the way forward, but I am not convinced, and I don't deploy Vagrant to production(containers might be the future for deployment but Vagrant in it's current state isn't). I don't use vagrant in production because I use a Ubuntu box for development and rvm/bundler, virtualenv/pip...cover my needs for maintaining different and isolated dev environments.
← PreviousPage 2 of 34Next →