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:
- `gpython` is Python interpreter with support for lightweight threads.
- `go` spawns lightweight thread.
- `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.
- `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::
Methods
-------
`method` decorator allows to define methods separate from class.
`func` decorator allows to define methods separate from class.
For example::
@method(MyClass)
@func(MyClass)
def my_method(self, ...):
...
will define `MyClass.my_method()`.
`func` can be also used on just functions, for example::
@func
def my_function(...):
...
Defer / recover / panic
-----------------------
......@@ -129,8 +135,8 @@ Go-style error handling, for example::
But `recover` and `panic` are probably of less utility since they can be
practically natively modelled with `try`/`except`.
If `defer` is used, the function that uses it must be wrapped with `@func` or
`@method` decorators.
If `defer` is used, the function that uses it must be wrapped with `@func`
decorator.
Import
------
......
......@@ -21,7 +21,7 @@
- `go` spawns lightweight thread.
- `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.
- `gimport` allows to import python modules by full path in a Go workspace.
......@@ -53,17 +53,44 @@ from golang._pycompat import im_class
# 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 it is required by defer. Usage:
#
# @func
# def my_function(...):
# ...
#
# For example:
# @func can be also used to define methods separate from class, for example:
#
# @method(MyClass)
# @func(MyClass)
# 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):
# wrap f with @func, so that e.g. defer works automatically.
f = func(f)
# wrap f with @_func, so that e.g. defer works automatically.
f = _func(f)
if isinstance(f, (staticmethod, classmethod)):
func_name = f.__func__.__name__
......@@ -72,19 +99,8 @@ def method(cls):
setattr(cls, func_name, f)
return deco
# panic stops normal execution of current goroutine.
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):
# _func serves @func.
def _func(f):
# @staticmethod & friends require special care:
# unpack f first to original func and then repack back after wrapping.
fclass = None
......
......@@ -384,25 +384,25 @@ def test_select():
def test_method():
# test how @method works
# this also implicitly tests @func, since @method uses that.
# test how @func(cls) works
# this also implicitly tests just @func, since @func(cls) uses that.
class MyClass:
def __init__(self, v):
self.v = v
@method(MyClass)
@method(MyClass) # @method(cls) = @func(cls); deprecated and to be removed
def zzz(self, v, x=2, **kkkkwww):
assert self.v == v
return v + 1
@method(MyClass)
@func(MyClass)
@staticmethod
def mstatic(v):
assert v == 5
return v + 1
@method(MyClass)
@func(MyClass)
@classmethod
def mcls(cls, v):
assert cls is MyClass
......@@ -414,7 +414,7 @@ def test_method():
assert obj.mstatic(5) == 5 + 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 MyClass.zzz.__module__ == __name__
......@@ -564,15 +564,15 @@ def test_deferrecover():
assert v == ['not recovered']
# ---- defer in @method(x) ----
# ---- defer in @func(x) ----
# defer in @method
# defer in @func(cls)
v = []
class MyClass:
pass
@method(MyClass)
@func(MyClass)
def zzz(self):
defer(lambda: v.append(1))
defer(lambda: v.append(2))
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2018 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com>
# Copyright (C) 2018-2019 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
......@@ -28,7 +28,7 @@ from __future__ import print_function
import re, io, numpy as np
from collections import OrderedDict
from golang import method
from golang import func
# Benchmark is a collection of benchmark lines.
......@@ -173,7 +173,7 @@ def _parse_benchline(linev):
# "user-s/op" -> "user-s/op", 1
#
# returns -> Unit, scale.
@method(Unit)
@func(Unit)
def normalize(self):
# split unit string into prefix and just unit
unit = self.unit
......@@ -328,7 +328,7 @@ def xload_file(path):
#
# returns ordered {} labelkey -> Benchmark.
# labelkey = () of (k,v) with k from label_list.
@method(Benchmark)
@func(Benchmark)
def bylabel(self, label_list):
bylabel = OrderedDict() # labelkey -> Benchmark
......@@ -351,7 +351,7 @@ def bylabel(self, label_list):
# with the same name.
#
# returns ordered {} name -> Benchmark.
@method(Benchmark)
@func(Benchmark)
def byname(self):
byname = OrderedDict() # name -> Benchmark
......@@ -368,7 +368,7 @@ def byname(self):
# the same measurements unit.
#
# returns ordered {} unit -> Benchmark.
@method(Benchmark)
@func(Benchmark)
def byunit(self):
byunit = OrderedDict() # unit -> Benchmark
......@@ -396,7 +396,7 @@ def byunit(self):
# all original values must have units compatible with the conversion.
#
# returns -> new Benchmark with converted units.
@method(Benchmark)
@func(Benchmark)
def convert_unit(self, unit):
B = Benchmark()
U = Unit(unit)
......@@ -420,7 +420,7 @@ def convert_unit(self, unit):
#
# all values must have the same units (use .byunit() to prepare).
# returns Stats.
@method(Benchmark)
@func(Benchmark)
def stats(self):
unit = None
vv = []
......@@ -439,24 +439,24 @@ def stats(self):
# ----------------------------------------
@method(BenchLine)
@func(BenchLine)
def __repr__(self):
# XXX +labels
return 'BenchLine(%r, %d, %r)' % (self.name, self.niter, self.measurev)
@method(Measure)
@func(Measure)
def __repr__(self):
return 'Measure(%r, %r)' % (self.value, self.unit)
@method(Unit)
@func(Unit)
def __repr__(self):
return 'Unit(%r)' % (self.unit)
@method(Unit)
@func(Unit)
def __str__(self):
return self.unit
@method(Stats)
@func(Stats)
def __str__(self):
delta = max(self.max - self.avg, self.avg - self.min)
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