Commit 942ee900 authored by Kirill Smelkov's avatar Kirill Smelkov

golang: Deprecate @method(cls) in favour of @func(cls)

Since we already have func (see 5146eb0b "Add support for defer &
recover") we can use @func for both plain functions and for methods.

For example instead of

	@func
	def my_function(...):
	    ...

	@method(MyClass)			<-- NOTE
	def my_method(self, ...):
	    ...

have it as

	@func
	def my_function(...):
	    ...

	@func(MyClass)				<-- NOTE
	def my_method(self, ...):
	    ...

which looks more similar to Go and exposes less golang keywords to a user.
parent 6b4990f6
...@@ -7,7 +7,7 @@ Package `golang` provides Go-like features for Python: ...@@ -7,7 +7,7 @@ Package `golang` provides Go-like features for Python:
- `gpython` is Python interpreter with support for lightweight threads. - `gpython` is Python interpreter with support for lightweight threads.
- `go` spawns lightweight thread. - `go` spawns lightweight thread.
- `chan` and `select` provide channels with Go semantic. - `chan` and `select` provide channels with Go semantic.
- `method` allows to define methods separate from class. - `func` allows to define methods separate from class.
- `defer` allows to schedule a cleanup from the main control flow. - `defer` allows to schedule a cleanup from the main control flow.
- `gimport` allows to import python modules by full path in a Go workspace. - `gimport` allows to import python modules by full path in a Go workspace.
...@@ -87,16 +87,22 @@ used to multiplex on several channels. For example:: ...@@ -87,16 +87,22 @@ used to multiplex on several channels. For example::
Methods Methods
------- -------
`method` decorator allows to define methods separate from class. `func` decorator allows to define methods separate from class.
For example:: For example::
@method(MyClass) @func(MyClass)
def my_method(self, ...): def my_method(self, ...):
... ...
will define `MyClass.my_method()`. will define `MyClass.my_method()`.
`func` can be also used on just functions, for example::
@func
def my_function(...):
...
Defer / recover / panic Defer / recover / panic
----------------------- -----------------------
...@@ -129,8 +135,8 @@ Go-style error handling, for example:: ...@@ -129,8 +135,8 @@ Go-style error handling, for example::
But `recover` and `panic` are probably of less utility since they can be But `recover` and `panic` are probably of less utility since they can be
practically natively modelled with `try`/`except`. practically natively modelled with `try`/`except`.
If `defer` is used, the function that uses it must be wrapped with `@func` or If `defer` is used, the function that uses it must be wrapped with `@func`
`@method` decorators. decorator.
Import Import
------ ------
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
- `go` spawns lightweight thread. - `go` spawns lightweight thread.
- `chan` and `select` provide channels with Go semantic. - `chan` and `select` provide channels with Go semantic.
- `method` allows to define methods separate from class. - `func` allows to define methods separate from class.
- `defer` allows to schedule a cleanup from the main control flow. - `defer` allows to schedule a cleanup from the main control flow.
- `gimport` allows to import python modules by full path in a Go workspace. - `gimport` allows to import python modules by full path in a Go workspace.
...@@ -53,17 +53,44 @@ from golang._pycompat import im_class ...@@ -53,17 +53,44 @@ from golang._pycompat import im_class
# and puts everything from golang.__all__ to __builtins__. # and puts everything from golang.__all__ to __builtins__.
# method decorator allows to define methods separate from class. # panic stops normal execution of current goroutine.
def panic(arg):
raise _PanicError(arg)
class _PanicError(Exception):
pass
def method(cls):
from warnings import warn
warn("@method(cls) is deprecated in favour of @func(cls)", DeprecationWarning, stacklevel=2)
return func(cls)
# @func is a necessary decorator for functions for selected golang features to work.
# #
# For example: # For example it is required by defer. Usage:
# #
# @method(MyClass) # @func
# def my_function(...):
# ...
#
# @func can be also used to define methods separate from class, for example:
#
# @func(MyClass)
# def my_method(self, ...): # def my_method(self, ...):
# ... # ...
def method(cls): def func(f):
if inspect.isclass(f):
return _meth(f)
else:
return _func(f)
# _meth serves @func(cls).
def _meth(cls):
def deco(f): def deco(f):
# wrap f with @func, so that e.g. defer works automatically. # wrap f with @_func, so that e.g. defer works automatically.
f = func(f) f = _func(f)
if isinstance(f, (staticmethod, classmethod)): if isinstance(f, (staticmethod, classmethod)):
func_name = f.__func__.__name__ func_name = f.__func__.__name__
...@@ -72,19 +99,8 @@ def method(cls): ...@@ -72,19 +99,8 @@ def method(cls):
setattr(cls, func_name, f) setattr(cls, func_name, f)
return deco return deco
# _func serves @func.
# panic stops normal execution of current goroutine. def _func(f):
def panic(arg):
raise _PanicError(arg)
class _PanicError(Exception):
pass
# @func is a necessary decorator for functions for selected golang features to work.
#
# It is needed for defer.
def func(f):
# @staticmethod & friends require special care: # @staticmethod & friends require special care:
# unpack f first to original func and then repack back after wrapping. # unpack f first to original func and then repack back after wrapping.
fclass = None fclass = None
......
...@@ -384,25 +384,25 @@ def test_select(): ...@@ -384,25 +384,25 @@ def test_select():
def test_method(): def test_method():
# test how @method works # test how @func(cls) works
# this also implicitly tests @func, since @method uses that. # this also implicitly tests just @func, since @func(cls) uses that.
class MyClass: class MyClass:
def __init__(self, v): def __init__(self, v):
self.v = v self.v = v
@method(MyClass) @method(MyClass) # @method(cls) = @func(cls); deprecated and to be removed
def zzz(self, v, x=2, **kkkkwww): def zzz(self, v, x=2, **kkkkwww):
assert self.v == v assert self.v == v
return v + 1 return v + 1
@method(MyClass) @func(MyClass)
@staticmethod @staticmethod
def mstatic(v): def mstatic(v):
assert v == 5 assert v == 5
return v + 1 return v + 1
@method(MyClass) @func(MyClass)
@classmethod @classmethod
def mcls(cls, v): def mcls(cls, v):
assert cls is MyClass assert cls is MyClass
...@@ -414,7 +414,7 @@ def test_method(): ...@@ -414,7 +414,7 @@ def test_method():
assert obj.mstatic(5) == 5 + 1 assert obj.mstatic(5) == 5 + 1
assert obj.mcls(7) == 7 + 1 assert obj.mcls(7) == 7 + 1
# this tests that @func (used by @method) preserves decorated function signature # this tests that @func (used by @func(cls)) preserves decorated function signature
assert inspect.formatargspec(*inspect.getargspec(MyClass.zzz)) == '(self, v, x=2, **kkkkwww)' assert inspect.formatargspec(*inspect.getargspec(MyClass.zzz)) == '(self, v, x=2, **kkkkwww)'
assert MyClass.zzz.__module__ == __name__ assert MyClass.zzz.__module__ == __name__
...@@ -564,15 +564,15 @@ def test_deferrecover(): ...@@ -564,15 +564,15 @@ def test_deferrecover():
assert v == ['not recovered'] assert v == ['not recovered']
# ---- defer in @method(x) ---- # ---- defer in @func(x) ----
# defer in @method # defer in @func(cls)
v = [] v = []
class MyClass: class MyClass:
pass pass
@method(MyClass) @func(MyClass)
def zzz(self): def zzz(self):
defer(lambda: v.append(1)) defer(lambda: v.append(1))
defer(lambda: v.append(2)) defer(lambda: v.append(2))
......
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2018 Nexedi SA and Contributors. # Copyright (C) 2018-2019 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com> # Kirill Smelkov <kirr@nexedi.com>
# #
# This program is free software: you can Use, Study, Modify and Redistribute # This program is free software: you can Use, Study, Modify and Redistribute
...@@ -28,7 +28,7 @@ from __future__ import print_function ...@@ -28,7 +28,7 @@ from __future__ import print_function
import re, io, numpy as np import re, io, numpy as np
from collections import OrderedDict from collections import OrderedDict
from golang import method from golang import func
# Benchmark is a collection of benchmark lines. # Benchmark is a collection of benchmark lines.
...@@ -173,7 +173,7 @@ def _parse_benchline(linev): ...@@ -173,7 +173,7 @@ def _parse_benchline(linev):
# "user-s/op" -> "user-s/op", 1 # "user-s/op" -> "user-s/op", 1
# #
# returns -> Unit, scale. # returns -> Unit, scale.
@method(Unit) @func(Unit)
def normalize(self): def normalize(self):
# split unit string into prefix and just unit # split unit string into prefix and just unit
unit = self.unit unit = self.unit
...@@ -328,7 +328,7 @@ def xload_file(path): ...@@ -328,7 +328,7 @@ def xload_file(path):
# #
# returns ordered {} labelkey -> Benchmark. # returns ordered {} labelkey -> Benchmark.
# labelkey = () of (k,v) with k from label_list. # labelkey = () of (k,v) with k from label_list.
@method(Benchmark) @func(Benchmark)
def bylabel(self, label_list): def bylabel(self, label_list):
bylabel = OrderedDict() # labelkey -> Benchmark bylabel = OrderedDict() # labelkey -> Benchmark
...@@ -351,7 +351,7 @@ def bylabel(self, label_list): ...@@ -351,7 +351,7 @@ def bylabel(self, label_list):
# with the same name. # with the same name.
# #
# returns ordered {} name -> Benchmark. # returns ordered {} name -> Benchmark.
@method(Benchmark) @func(Benchmark)
def byname(self): def byname(self):
byname = OrderedDict() # name -> Benchmark byname = OrderedDict() # name -> Benchmark
...@@ -368,7 +368,7 @@ def byname(self): ...@@ -368,7 +368,7 @@ def byname(self):
# the same measurements unit. # the same measurements unit.
# #
# returns ordered {} unit -> Benchmark. # returns ordered {} unit -> Benchmark.
@method(Benchmark) @func(Benchmark)
def byunit(self): def byunit(self):
byunit = OrderedDict() # unit -> Benchmark byunit = OrderedDict() # unit -> Benchmark
...@@ -396,7 +396,7 @@ def byunit(self): ...@@ -396,7 +396,7 @@ def byunit(self):
# all original values must have units compatible with the conversion. # all original values must have units compatible with the conversion.
# #
# returns -> new Benchmark with converted units. # returns -> new Benchmark with converted units.
@method(Benchmark) @func(Benchmark)
def convert_unit(self, unit): def convert_unit(self, unit):
B = Benchmark() B = Benchmark()
U = Unit(unit) U = Unit(unit)
...@@ -420,7 +420,7 @@ def convert_unit(self, unit): ...@@ -420,7 +420,7 @@ def convert_unit(self, unit):
# #
# all values must have the same units (use .byunit() to prepare). # all values must have the same units (use .byunit() to prepare).
# returns Stats. # returns Stats.
@method(Benchmark) @func(Benchmark)
def stats(self): def stats(self):
unit = None unit = None
vv = [] vv = []
...@@ -439,24 +439,24 @@ def stats(self): ...@@ -439,24 +439,24 @@ def stats(self):
# ---------------------------------------- # ----------------------------------------
@method(BenchLine) @func(BenchLine)
def __repr__(self): def __repr__(self):
# XXX +labels # XXX +labels
return 'BenchLine(%r, %d, %r)' % (self.name, self.niter, self.measurev) return 'BenchLine(%r, %d, %r)' % (self.name, self.niter, self.measurev)
@method(Measure) @func(Measure)
def __repr__(self): def __repr__(self):
return 'Measure(%r, %r)' % (self.value, self.unit) return 'Measure(%r, %r)' % (self.value, self.unit)
@method(Unit) @func(Unit)
def __repr__(self): def __repr__(self):
return 'Unit(%r)' % (self.unit) return 'Unit(%r)' % (self.unit)
@method(Unit) @func(Unit)
def __str__(self): def __str__(self):
return self.unit return self.unit
@method(Stats) @func(Stats)
def __str__(self): def __str__(self):
delta = max(self.max - self.avg, self.avg - self.min) delta = max(self.max - self.avg, self.avg - self.min)
return '%.2f ±%2.0f%%' % (self.avg, 100. * delta / self.avg) return '%.2f ±%2.0f%%' % (self.avg, 100. * delta / self.avg)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment