Golang equivalent of Python's context manager

1,085 views
Skip to first unread message

Mirko Friedenhagen

unread,
Sep 12, 2018, 7:58:19 AM9/12/18
to golang-nuts
Hello,

in Python I may define context managers which do stuff before and after an action has taken place.

E.g.:

```
class MyContext(object):
    def __enter__(self):
        print("Entering context")
    def __exit__(self, extype_unused, value_unused, traceback_unused):
        print("Exiting context")

if __name__ == "__main__":
    with MyContext():
        print("Inside the context")
```

will print:
Entering context
Inside the context
Exiting context

A Golang equivalent I came up with is something like (see https://play.golang.org/p/epBGtwZWuln as well)

```
package main

import "log"

type Context interface {
__entry__()
__exit__()
}

func WithContext(context Context, f func() error) error {
context.__entry__()
defer context.__exit__()
return f()
}

type MyContext struct {
message string
}

func (context *MyContext) __entry__() {
log.Print("Entering ", context.message)
}

func (context *MyContext) __exit__() {
log.Print("Exiting ", context.message)
}

func main() {
var a string
WithContext(&MyContext{"Hallo"}, func() error {
log.Print("Inside context")
a = "Var from context"
return nil
})
log.Print(a)
}
```

Is this idiomatic or are there better ways to implement stuff like this?

Regards
Mirko

robert engels

unread,
Sep 12, 2018, 8:10:58 AM9/12/18
to Mirko Friedenhagen, golang-nuts
I think it would be problematic, as the callee signature is limited - func() error - and with lack of method overloading you will need a lot of WithXXXX methods…

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Sebastien Binet

unread,
Sep 12, 2018, 8:19:01 AM9/12/18
to ren...@ix.netcom.com, mfried...@gmx.de, golang-nuts
On Wed, Sep 12, 2018 at 2:10 PM robert engels <ren...@ix.netcom.com> wrote:
I think it would be problematic, as the callee signature is limited - func() error - and with lack of method overloading you will need a lot of WithXXXX methods…

apart from the non-Go idiomatic names for the enter/exit interface method set, I'd say it's fine.
(well, Context is now kind of "reserved" to the context.Context interface, but ok.)

it's also relatively easy to adapt to other functions that do not return an error (as you show in your example.)
that's what golang.org/x/sync/errgroup does:

> and with lack of method overloading you will need a lot of WithXXXX methods…
no need for that: it's an interface.

-s

robert engels

unread,
Sep 12, 2018, 8:23:33 AM9/12/18
to Sebastien Binet, mfried...@gmx.de, golang-nuts
I am pretty sure that is not correct, I am referring to:

func WithContext(context Context, f func() error) error {
context.__entry__()
defer context.__exit__()
return f()
}

You will need a lot of WithContext methods - based on the different signatures of function f.

See the main method… I am assuming this is attempting to be a general facility - and thus the signature of f will vary.

Sebastien Binet

unread,
Sep 12, 2018, 8:35:36 AM9/12/18
to ren...@ix.netcom.com, mfried...@gmx.de, golang-nuts
On Wed, Sep 12, 2018 at 2:23 PM robert engels <ren...@ix.netcom.com> wrote:
I am pretty sure that is not correct, I am referring to:

func WithContext(context Context, f func() error) error {
context.__entry__()
defer context.__exit__()
return f()
}

You will need a lot of WithContext methods - based on the different signatures of function f.

See the main method… I am assuming this is attempting to be a general facility - and thus the signature of f will vary.

you just need to wrap it with a closure, just as Eric did:

e.g.:
 func QueryDB(a,b int) error { ... }
 
 WithContext(ctx, func() error {
    return QueryDB(42, 666)
 })

and voila.

-s

robert engels

unread,
Sep 12, 2018, 8:49:49 AM9/12/18
to Sebastien Binet, mfried...@gmx.de, golang-nuts
Yes, you are correct. My bad :)

robert engels

unread,
Sep 12, 2018, 8:51:11 AM9/12/18
to Sebastien Binet, mfried...@gmx.de, golang-nuts
Too early, and the coffee had not kicked in...

robert engels

unread,
Sep 12, 2018, 8:55:35 AM9/12/18
to Sebastien Binet, mfried...@gmx.de, golang-nuts
Ok, now the coffee has kicked in, and it still has problems, because you cannot call a method and use a return value other than error without multiple signatures, unless you move all of the return values outside of the closure - like his example does with var a.

This would be extremely tedious (and probably error prone) to use IMO, especially with Go’s multiple return values.

Sebastien Binet

unread,
Sep 12, 2018, 9:18:38 AM9/12/18
to ren...@ix.netcom.com, mfried...@gmx.de, golang-nuts
On Wed, Sep 12, 2018 at 2:55 PM robert engels <ren...@ix.netcom.com> wrote:
Ok, now the coffee has kicked in, and it still has problems, because you cannot call a method and use a return value other than error without multiple signatures, unless you move all of the return values outside of the closure - like his example does with var a. 

This would be extremely tedious (and probably error prone) to use IMO, especially with Go’s multiple return values.

one has to be careful, yes. but it's not completely out of reach.

func Foo(a, b int) (int, error) { ... }

func Do() {
    var out = 0
    With(ctx, func() error {
        var err error
        out, err = Foo(42, 66)
        return err
    })
}

closures are great! (like fezzes and bowties, actually :P)

-s

robert engels

unread,
Sep 12, 2018, 9:37:12 AM9/12/18
to Sebastien Binet, mfried...@gmx.de, golang-nuts
Agree it can be done - not sure I would want to maintain that in a large system as you are paying the LOC penalty at every call site. Seems easier to just add the context callback in the methods themselves so then you are in control of the code penalty.

Mirko Friedenhagen

unread,
Sep 12, 2018, 3:12:57 PM9/12/18
to golang-nuts
First of all, thanks for all the answers. A standard use case in Python is to use with the a closing context manager which will just call close on a given object. That one could be easily done with a defer statement. However, coming from Java lately, retrieving a DB-connection from a pool, opening a transaction and committing/aborting and returningafterwards seems something which could be handled with such a construct. How would you do this in Golang?

Sam Whited

unread,
Sep 12, 2018, 3:36:06 PM9/12/18
to golan...@googlegroups.com
On Wed, Sep 12, 2018, at 14:12, Mirko Friedenhagen wrote:
> However, coming from Java lately, retrieving a DB-connection from a pool,
> opening a transaction and committing/aborting and returningafterwards seems
> something which could be handled with such a construct. How would you do
> this in Golang?

I frequently do something similar to this:

https://gist.github.com/SamWhited/d81d081aed0351a1c1d128acf3a16b5c

(there may be some extra parameters, I cut out some row-level-security stuff from something being used in prod and pasted everything into the gist without really checking it)

—Sam

Justin Israel

unread,
Sep 12, 2018, 4:12:23 PM9/12/18
to Sam Whited, golan...@googlegroups.com
For about the same amount of typing that you have to do to create a closure for using with a predefined WithContext handler, I tend to do this inline:

func start() { fmt.Println("start") }
func stop() { fmt.Println("stop") }

func main() {
    func() (string, error) {
        start()
        defer stop()
        fmt.Println("run")
        return "hi", nil
    }()
}


It means I can return any signature, as long as I have the enter/exit functions defined already. 

Justin 

Mirko Friedenhagen

unread,
Sep 12, 2018, 6:13:35 PM9/12/18
to golang-nuts
Thanks again. I think some of the use cases, especially those which implement some kind of finally (which includes locking use case) may just be implemented with simple functions. At least throwing exceptions is a rare event in Golang (panicking is what I meant with rare).

Are closures, like the one in the main method idiomatic?

Justin Israel

unread,
Sep 12, 2018, 10:51:38 PM9/12/18
to Mirko Friedenhagen, golang-nuts


On Thu, Sep 13, 2018, 10:13 AM Mirko Friedenhagen <mfried...@gmx.de> wrote:
Thanks again. I think some of the use cases, especially those which implement some kind of finally (which includes locking use case) may just be implemented with simple functions. At least throwing exceptions is a rare event in Golang (panicking is what I meant with rare).

Are closures, like the one in the main method idiomatic?

I don't see why not. 
Also when I was using the boltdb api, I found it relies heavily on passing function closures to the view and batch functions. The signature of the function can be minimal like just returning and error, and you can capture arbitrary return values just by declaring them in the outer scope and setting them in the closure. 
Reply all
Reply to author
Forward
0 new messages