Coder Social home page Coder Social logo

fn.py's Introduction

Fn.py: enjoy FP in Python

PyPI PyPI - Downloads Github Discussions

Installation

To install fn.py, simply:

$ pip install fn.py

You can also build library from source

$ git clone https://github.com/fnpy/fn.py.git
$ pip install -e fn.py

What is it?

Despite the fact that Python is not a pure-functional programming language, it is multi-paradigm and gives you enough freedom to take advantage of a functional approach. There are theoretical and practical advantages to the functional style:

  • Formal provability
  • Modularity
  • Composability
  • Ease of debugging and testing

Fn.py library provides you with the missing "batteries" to get the maximum from a functional approach, even in mostly-imperative softwares.

You can find more about the functional approach from my Pycon UA 2012 talks: Functional Programming with Python.

Scala-style lambdas definition

>>> from fn import _
>>> from fn.op import zipwith
>>> from itertools import repeat

>>> list(map(_ * 2, range(5)))
[0, 2, 4, 6, 8]
>>> list(filter(_ < 10, [9,10,11]))
[9]
>>> list(zipwith(_ + _)([0,1,2], repeat(10)))
[10, 11, 12]

You can find more examples of _ in test cases (attributes resolving, method calling, slicing).

Attention! If you work in an interactive python shell, _ can be assigned to the latest output and you'll get unpredictable results. In this case, you can use X instead with from fn import _ as X.

If you are not sure what your function is going to do, print it:

>>> print(_ + 2)
(x1) => (x1 + 2)
>>> print(_ + _ * _)
(x1, x2, x3) => (x1 + (x2 * x3))

Note that _ will fail with ArityError (TypeError subclass) when called with the wrong number of arguments, so as to avoid errors:

>>> (_ + _)(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fn/underscore.py", line 82, in __call__
    raise ArityError(self, self._arity, len(args))
fn.underscore.ArityError: (_ + _) expected 2 arguments, got 1

Persistent data structures

Attention: Persistent data structures are under active development.

A persistent data structure always preserves its previous version when it is modified (more on Wikipedia). Each operation thus yields a new updated structure instead of performing in-place modifications (all previous versions are potentially available or GC-ed when possible).

>>> from fn.immutable import SkewHeap
>>> s1 = SkewHeap(10)
>>> s2 = s1.insert(20)
>>> s2
<fn.immutable.heap.SkewHeap object at 0x...>
>>> s3 = s2.insert(30)
>>> s3
<fn.immutable.heap.SkewHeap object at 0x...>
>>> id(s3) != id(s2)
True
>>> s3.extract()
(10, <fn.immutable.heap.SkewHeap object at 0x...>)
>>> s3.extract() # <-- s3 isn't changed
(10, <fn.immutable.heap.SkewHeap object at 0x...>)

If you think I'm totally crazy and it will work despairingly slow, just give it 5 minutes. Relax, take a deep breath and read about a few techniques that make persistent data structures fast and efficient: structural sharing and path copying. To see how it works in "pictures", you can check the great slides from Zach Allaun's talk (StrangeLoop 2013): "Functional Vectors, Maps And Sets In Julia". And, if you are brave enough, go and read:

Immutable data structures available in fn.immutable:

  • LinkedList: the most "obvious" persistent data structure, used as building block for other list-based structures (stack, queue)
  • Stack: wraps linked list implementation with well-known pop/push API
  • Queue: uses two linked lists and lazy copy to provide O(1) enqueue and dequeue operations
  • Deque (in progress): "Confluently Persistent Deques via Data Structural Bootstrapping"
  • Deque based on FingerTree data structure (see more information below)
  • Vector: O(log32(n)) access to elements by index (which is near-O(1) for reasonable vector size), implementation is based on BitmappedTrie, almost drop-in replacement for built-in Python list
  • SkewHeap: self-adjusting heap implemented as a binary tree with specific branching model, uses heap merge as basic operation, more information - "Self-adjusting heaps"
  • PairingHeap: "The Pairing-Heap: A New Form of Self-Adjusting Heap"
  • Dict (in progress): persistent hash map implementation based on BitmappedTrie
  • FingerTree (in progress): "Finger Trees: A Simple General-purpose Data Structure"

To get a clearer vision of how persistent heaps work (SkewHeap and PairingHeap), you can look at slides from my talk "Union-based heaps" (with analyzed data structures definitions in Python and Haskell).

Note. Most functional languages use persistent data structures as basic building blocks, well-known examples are Clojure, Haskell and Scala. Clojure community puts much effort to popularize programming based on the idea of data immutability. There are few amazing talk given by Rich Hickey (creator of Clojure), you can check them to find answers on both questions "How?" and "Why?":

Streams and infinite sequences declaration

Lazy-evaluated Scala-style streams. Basic idea: evaluate each new element "on demand" and share consumed elements between all created iterators. A Stream instance supports << to push new elements.

>>> from fn import Stream

>>> s = Stream() << [1,2,3,4,5]
>>> list(s)
[1, 2, 3, 4, 5]
>>> s[1]
2
>>> list(s[0:2])
[1, 2]

>>> s = Stream() << range(6) << [6,7]
>>> list(s)
[0, 1, 2, 3, 4, 5, 6, 7]

>>> def gen():
...     yield 1
...     yield 2
...     yield 3

>>> s = Stream() << gen << (4,5)
>>> list(s)
[1, 2, 3, 4, 5]

Lazy-evaluated streams are useful for infinite sequences, e.g. the fibonacci sequence can be computed as:

>>> from fn.iters import take, drop, map
>>> from operator import add

>>> f = Stream()
>>> fib = f << [0, 1] << map(add, f, drop(1, f))

>>> list(take(10, fib))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> fib[20]
6765
>>> list(fib[30:35])
[832040, 1346269, 2178309, 3524578, 5702887]

Trampolines decorator

fn.recur.tco simulates TCO (tail call optimization). Let's start with a simple example of recursive factorial computation:

>>> def fact(n):
...    if n == 0: return 1
...    return n * fact(n-1)

This variant works, but it's really inefficient. Why? It will consume too much memory, linear in the depth of the recursion: if you execute this function with a big n (more than sys.getrecursionlimit()) CPython will fail:

>>> import sys
>>> fact(sys.getrecursionlimit() * 2)
Traceback (most recent call last):
    ...
RecursionError: maximum recursion depth exceeded ...

Which is good, as it prevents you from terrible mistakes in your code.

How can we optimize this function? Easy, let's transform it to use a tail call:

def fact(n, acc=1):
    if n == 0: return acc
    return fact(n-1, acc*n)

Is this variant better? Yes, because you don't need to remember previous values (local variables) to get the final result. More about tail call optimization on Wikipedia. But... the Python interpreter will execute this function the same way as the previous one, so you won't win anything here.

fn.recur.tco allows you to optimize a bit this tail call recursion (using a "trampoline" approach):

>>> from fn import recur

>>> @recur.tco
... def fact(n, acc=1):
...    if n == 0: return False, acc
...    return True, (n-1, acc*n)

@recur.tco executes your function in a while loop and checks the output:

  • (False, result) means that we finished,
  • (True, args, kwargs) means that we need to recurse,
  • (func, args, kwargs) switches the function executed inside the while loop.

Example for the third case, to recursively check the parity of a number:

>>> @recur.tco
... def even(x):
...     if x == 0: return False, True
...     return odd, (x-1,)
...
>>> @recur.tco
... def odd(x):
...     if x == 0: return False, False
...     return even, (x-1,)
...
>>> even(100000)
True

Attention: be careful with mutable/immutable data structures processing.

Itertools recipes

fn.uniform unifies generator functions between Python versions (use generator versions of map, filter, reduce, zip, range, filterfalse, zip_longest, backported accumulate).

fn.iters offers high-level recipes for working with iterators, most of them are from the itertools docs and adapted for Python 2+/3+.

  • take, drop
  • takelast, droplast
  • head (alias: first), tail (alias: rest)
  • second, ffirst
  • compact, reject
  • every, some
  • iterate
  • consume
  • nth
  • padnone, ncycles
  • repeatfunc
  • grouper, powerset, pairwise
  • roundrobin
  • partition, splitat, splitby
  • flatten
  • iter_except
  • first_true

See the docstrings and tests for more information.

High-level operations with functions

fn.F wraps functions for easy-to-use partial application and composition:

>>> from fn import F
>>> from operator import add, mul

# F(f, *args) means partial application
# same as functools.partial but returns fn.F instance
>>> F(add, 1)(10)
11

# F << F means functions composition,
# so (F(f) << g)(x) == f(g(x))
>>> f = F(add, 1) << F(mul, 100)
>>> list(map(f, [0, 1, 2]))
[1, 101, 201]
>>> list(map(F() << str << (_ ** 2) << (_ + 1), range(3)))
['1', '4', '9']

You can also pipe functions with >>:

>>> from fn.iters import filter, range

>>> func = F() >> (filter, _ < 6) >> sum
>>> func(range(10))
15

You can find more examples in the fn._ implementation.

fn.op.apply executes a function with given args and kwargs. fn.op.flip wraps a function, flipping the order of its arguments.

>>> from fn.op import apply, flip
>>> from operator import add, sub

>>> apply(add, [1, 2])
3
>>> flip(sub)(20, 10)
-10
>>> list(map(apply, [add, mul], [(1 ,2), (10, 20)]))
[3, 200]

fn.op.foldl and fn.op.foldr create a reducer from a function of two arguments (think of it as curried reduce).

>>> from fn import op
>>> op.foldl(_ + _)([1,2,3])
6
>>> mult = op.foldr(_ * _, 1)
>>> mult([1,2,3])
6
>>> op.foldr(op.call, 0 )([_ ** 2, _ + 10])
100
>>> op.foldr(op.call, 10)([_ ** 2, _ + 10])
400

Function currying

>>> from fn.func import curried
>>> @curried
... def sum5(a, b, c, d, e):
...     return a + b + c + d + e
...
>>> sum5(1)(2)(3)(4)(5)
15
>>> sum5(1, 2, 3)(4, 5)
15

Functional style error-handling

fn.monad.Option represents optional values, each instance of Option can be either Full or Empty. It provides you with a simple way to write long computation sequences and get rid of many if/else blocks. See usage examples below.

Assume that you have a Request class that gives you a parameter value by its name, and you have to convert it to uppercase and strip it:

>>> class Request(dict):
...     def parameter(self, name):
...         return self.get(name, None)

>>> r = Request(testing="Fixed", empty="   ")
>>> param = r.parameter("testing")
>>> if param is None:
...     fixed = ""
... else:
...     param = param.strip()
...     if len(param) == 0:
...         fixed = ""
...     else:
...         fixed = param.upper()
>>> fixed
'FIXED'

Hmm, looks ugly.. But now with fn.monad.Option:

>>> from operator import methodcaller
>>> from fn.monad import optionable

>>> class Request(dict):
...     @optionable
...     def parameter(self, name):
...         return self.get(name, None)

>>> r = Request(testing="Fixed", empty="   ")
>>> (r
...     .parameter("testing")
...     .map(methodcaller("strip"))
...     .filter(len)
...     .map(methodcaller("upper"))
...     .get_or("")
... )
'FIXED'

Use or_call for more complex alternatives, for example:

from fn.monad import Option

request = dict(url="face.png", mimetype="PNG")
tp = (Option
    .from_value(request.get("type", None))  # check "type" key first
    .or_call(from_mimetype, request)  # or.. check "mimetype" key
    .or_call(from_extension, request)  # or... get "url" and check extension
    .get_or("application/undefined")
)

Work in progress

"Roadmap":

  • fn.monad.Either to deal with error logging
  • C-accelerator for most modules

Ideas to think about:

  • Scala-style for-yield loop to simplify long map/filter blocks

Contribute

If you find a bug:

  1. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
  2. Fork the repository on Github to start making your changes to the master branch (or branch off of it).
  3. Write a test which shows that the bug was fixed or that the feature works as expected.

If you like fixing bugs:

  1. Check for open issues with the label "Help Wanted" and either claim it or collaborate with those who have claimed it.
  2. Fork the repository on Github to start making your changes to the master branch (or branch off of it).
  3. Write a test which shows that the bug was fixed or that the feature works as expected.

How to contact the maintainers

  • Gitter: https://gitter.im/fnpy/fn.py
  • Jacob's (Organization Owner) Email: him <at> jacobandkate143.com
  • Alex's (Original Project Owner) Email: kachayev <at> gmail.com

fn.py's People

Contributors

adamsar avatar aitjcize avatar alphaho avatar artemisart avatar cad106uk avatar calebsmith avatar ccfontes avatar danielhfrank avatar digenis avatar gabrielpjordao avatar gitter-badger avatar howiezhao avatar jacobbridges avatar jahs avatar javadba avatar kachayev avatar kangol avatar katoken-0215 avatar medecau avatar mjkaye avatar pawroman avatar sbinq avatar soasme avatar w-m avatar z4y4ts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fn.py's Issues

Support for type annotations in `curried`

Hi, first thanks to everyone for continuing to maintain this library.

Currently when trying to use the curried decorator on a function with type annotations the following error is raised:
Traceback (most recent call last): File "<input>", line 1, in <module> File "C:\Python35\lib\inspect.py", line 1045, in getargspec raise ValueError("Function has keyword-only arguments or annotations" ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them

Swapping out getargspec() in curried for getfullargspec() does fix the issue and I'm happy to submit a PR with the change, but I'm not sure how best to write a test as type annotations are a Python3-only feature, as are keyword-only arguments.

Improve "methods" for underscore

When you write something like this

_.split(',')

you intuitively expect that at some point you are going to get a string s and excecute s.split(','). In practical terms you want this

_.split(',')("hello,world") # "hello,world".split(',') == ["hello", "world"]

instead you get strange behaviour that interprets things like this

_.split(',')("hello,world") # ','.split('"hello,world") == [',']

I prefer the first way of interpreting this. I've modified the code to achieve this:

def __getattr__(self, name):
    attr_name = _random_name()

    def method_proxy(*args, **kwargs):
        def f(x):
            method = getattr(x, name)
            return method(*args, **kwargs)

        return self.__class__(
            F(f) << F(self),
            "getattr(%s, %%(%s)r)" % (self._format, attr_name),
            dict(
                list(self._format_args.items()) + [(attr_name, name)]
            ),
            self._arity
        )

    return method_proxy

PyPI lib version (0.5.2) doesn't work with python 3.10

Installing PyPI version of this lib produces a lot of next messages:

Using cached fn.py-0.4.3.tar.gz (32 kB)
  Preparing metadata (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /Users/goloveshkokonstantin/venv/bin/python3.10 -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/setup.py'"'"'; __file__='"'"'/private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-pip-egg-info-n_rplav_
       cwd: /private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/
  Complete output (11 lines):
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "/private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/setup.py", line 6, in <module>
      import fn
    File "/private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/fn/__init__.py", line 1, in <module>
      from .stream import Stream
    File "/private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/fn/stream.py", line 9, in <module>
      from .iters import map, range
    File "/private/var/folders/2g/4nxj2yr14sx8nx_jf8yfkz6m0000gn/T/pip-install-thc9c89p/fn-py_d5536860a1c542198d6eae3fd653f26b/fn/iters.py", line 2, in <module>
      from collections import deque, Iterable
  ImportError: cannot import name 'Iterable' from 'collections' (/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/collections/__init__.py)
  ----------------------------------------

fn.func.curried does not work in Python 2

from fn.func import curried

@curried
def f(a, b):
    return a + b

print(f(1)(5))

The above code yields "6" in Python 3.5.1, but in Python 2.7.11 it produces the following error:

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    print(f(1)(5))
  File "/home/jacob/.pyenv/versions/2.7.11/lib/python2.7/site-packages/fn/func.py", line 83, in _curried
    return curried(partial(func, *args, **kwargs))
  File "/home/jacob/.pyenv/versions/2.7.11/lib/python2.7/site-packages/fn/func.py", line 69, in curried
    @wraps(func)
  File "/home/jacob/.pyenv/versions/2.7.11/lib/python2.7/functools.py", line 33, in update_wrapper
    setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute '__module__'

Serializable fn._

It would be useful for the underscore function to be serializable. The two primary use cases would be pickling for storage, but more commonly pickling to execute a function in parallel.

The following doesn't work using pickle or the more powerful dill package:

import pickle
import dill
from fn import _ as X

pickle.dumps(X + 1)
# ArityError: getattr((_ + 1), '__getstate__') expected 1 arguments, got 0

dill.dumps(X + 1)
# ArityError: getattr((_ + 1), '__getstate__') expected 1 arguments, got 0

f = X + 1
pickle.dumps(f)
# ArityError: getattr((_ + 1), '__getstate__') expected 1 arguments, got 0

dill.dumps(f)
# ArityError: getattr((_ + 1), '__getstate__') expected 1 arguments, got 0

dill.loads(dill.dumps(lambda x: x + 1))(1)
#2

My very specific use case is for https://github.com/EntilZha/PyFunctional where you can do this to run a map in parallel:

from functional import pseq

# This doesn't work
pseq.range(4).map(X + 1)

# This works
pseq.range(4).map(lambda x: x + 1)

How to install?

I take that the pypi repo is not being update with the code on this fork?

Setup Travis-CI (automated test runner)

An automated test runner would help when reviewing future pull requests, as well as testing the project against newer versions of Python.

  • Integrate Travis-CI with the project
    • Setup testing against Python 2.7.x, 3.1.x, 3.2.x, 3.3.x, 3.4.x, 3.5.x
    • Setup testing for new pull requests
    • Setup running a new test run against master on every commit

Unexpected behavior of `_` shortcut on __call__ execution

When I try to execute following code it throwing an error:

In [49]: from fn import _

In [50]: from fn import F

In [51]: f = F()

In [52]: a = f >> _.split >> _(',')

In [53]: a('1,2,3')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-53-3db68dd60b11> in <module>()
----> 1 a('1,2,3')

/path/to/package/fn/func.pyc in __call__(self, *args, **kwargs)
     53     def __call__(self, *args, **kwargs):
     54         """Overload apply operator"""
---> 55         return self.f(*args, **kwargs)
     56 
     57 

/path/to/package/fn/func.pyc in <lambda>(*args, **kwargs)
     34         in other methods.
     35         """
---> 36         return cls(lambda *args, **kwargs: f(g(*args, **kwargs)))
     37 
     38     def __ensure_callable(self, f):

TypeError: 'str' object is not callable

I expect that previous code should works like this:

a = lambda x: x.split(',')

Actually it works if we rewrite it a little bit:

In [61]: from fn import _

In [62]: from fn import F

In [63]: f = F()

In [64]: a = f >> _.split >> (lambda x: x(','))

In [65]: a('1,2,3')
Out[65]: ['1', '2', '3']

So, the problem in the method _Callable.__call__.
By default _calback=identity and we have following:

In [6]: from fn import _

In [7]: _(',')
Out[7]: ','

ImportError: Can't import name 'Iterable' from 'collections'

Hello, I have try to setup a tool in a linux server and it requires Python 3 and fn.py.
But when I try to install fn.py I have the error : ImportError: cannot import name 'Iterable' from 'collections' (/usr/lib/python3.10/collections/__init__.py)

I read another issue that concerned this problem but the problem is still present, do I have to redo the installation of my tool with another version of Python 3 or can an update of fn.py be done?

Thank you in advance !

The error in the shell :
esfvsrever4v6redv1rd

Deploy on PyPi

What is everyone's thoughts on deploying this project to the Python package index? I know one already exists, but we don't have access to change that one.

If we agree to make this fork available on PyPi, what should we change the name to?

Should _ fail with ArityError, or return a curried function?

Improperly handling arity can be a hard to track down source of error, especially when composing functions, but I would want (_ + _)(2) to return a function that looked like "(x1) => (2 + x1)" such that (_ + _)(2, 3) and (_ + _)(2)(3) would be equivalent and both result in 5.

I can understand the intent of the TypeError and it could actually be the best approach to ease problems dealing with composition and with standard python functions, e.g.

list(filter(_ < _, [9,10,11]))

would be problematic. However, it might be worth considering the curried behavior.

Phi

Hi, I have created this library called phi: https://github.com/cgarciae/phi

It has a modified version of fn.py's lambdas (it creates only single argument functions) but includes a bunch of other stuff to make life easier, specially to create fluent libraries. It was originally created as part of TensorBuilder which is a library based on TensorFlow for Deep Learning, but I decided to decouple it.

I would love some feedback from you since the FP community in Python is rather small.

Feel free to close the issue.

Versioning

Currently the project version is at 0.4.3 (here). Should we continue incrementing the current version (minor for bug fixes, major for new features), or should we bump the version to 1.0.0 and start "fresh"?

Weired behavior of underscore shortcut

When I created new lambda with _ underscore also mutates to that value:

In [1]: from fn import _

In [2]: _(",")
Out[2]: ','

In [3]: _
Out[3]: ','

Or

In [10]: from fn import _

In [11]: a = _ + 1

In [12]: a
Out[12]: (_ + 1)

In [13]: _
Out[13]: (_ + 1)

In [14]: a(1)
Out[14]: 2

In [15]: _
Out[15]: 2

I think '_' shouldn't be affected by calling external functions (like in 2 example) or calling itself.

Discuss: What coding style guide should fn.py use?

@fnpy/fn-py-team

We have a pending pull request (#13) to fix some PEP8 problems with the project, but before merging that into master it has been suggested that we review as a team what style guide to follow.

Once we decide upon a coding standard (such as max column length, alphabetical imports, snake case vs camel case, etc.) I will add it to the project's wiki. I don't think the linter you use should matter, just the style rules.


I think this is a productive topic, but it could easily reduce into people squabbling over personal preferences and maintenance further being delayed. So, with that in mind I am only going to let us discuss this for a week, maybe two weeks if some people don't get in their say quick enough. I just don't want this to deter development on the project longer than it should.

Python 3.11 error: `cannot import name 'getargspec' from 'inspect'`

Hi,

It seems that there were backward incompatible changes in the inspect module and the getargspec function has been completely removed breaking the underscore completely.

It is quite likely that the fix would be as easy as changing line 2 in fn/func.py from:

from inspect import getargspec

to

try:
    from inspect import getargspec
except ImportError:
    from inspect import getfullargspec as getargspec

If there is a chance that such a patch could introduced and published quickly I will be happy to test this and make a pull request.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.