<p align="center"> <img src="https://raw.githubusercontent.com/thyeem/foc/main/foc.png" height="180"/></p>
[![foc](https://img.shields.io/pypi/v/foc)](https://pypi.org/project/foc/)
# foc
_Func-Oriented Code_ or _Francis' Odd Collection_.
`foc` is a _non-frilled_ and _seamlessly integrated_ functional `Python` tool.
- provides a collection of ___higher-order functions___ and ___placeholder lambda syntax___ (`_`)
- provides an easy way to ___compose functions with symbols___. (`.` and `|`)
```python
>>> from foc import *
# in standard Python, we normally use,
>>> sum(map(lambda x: x+1, range(10)))
55
# 'foc' allows three more things:
>>> cf_(sum, map(_ + 1))(range(10)) # using the 'cf_' compose function
55
>>> (sum . map(_ + 1))(range(10)) # using '.' mathematical symbol (:P)
55
>>> range(10) | map(_ + 1) | sum # using '|' Unix pipeline style
55
# Scala-style placeholder syntax lambda expression
>>> (_ + 7)(3) # same as (lambda x: x + 7)(3)
10
# (3 + 4) * 6
>>> cf_(_ * 6, _ + 4)(3) # function.
42
>>> 3 | _ + 4 | _ * 6 # pipeline.
42
>>> ((_ * 6) . fx(_ + 4))(3) # dot. Wrap 'lambda expression' in 'fx' when using '.'.
42
```
Remember that it is only necessary to use `fx` _when using lambda expressions and `.`_.
That's all.
For more examples, see the [documentation](https://github.com/thyeem/foc/blob/main/foc/__init__.py#L150) provided with each function.
```python
>>> (rev . filter(even) . range)(10) # list(reversed(filter(even, range(10))))
[8, 6, 4, 2, 0]
>>> ((_ * 5) . nth(3) . range)(5) # range(5)[3] * 5
10
>>> (collect . filter(_ == "f"))("fun-on-functions") # list(filter(lambda x: x == "f", "fun-on-functions"))
['f', 'f']
# To use built-ins 'list' on the fly,
>>> (fx(list) . map(abs) . range)(-2, 3) # list(map(abs, range(-2, 3)))
[2, 1, 0, 1, 2]
>>> range(73, 82) | map(chr) | unchars # unchars(map(chr, range(73, 82)))
'IJKLMNOPQ'
```
## _Ouch_
[`ouch`](https://github.com/thyeem/ouch) is a collection of utilities that are based on and aligned with `foc`.
```python
from ouch import *
# soft/hard flatten
>>> flatten([1, [2, 3, (4, 5)]])
[1, 2, 3, (4, 5)]
>>> [1, [(2,), [[{3}, (x for x in range(3))]]]] | flat | collect
[1, 2, 3, 0, 1, 2]
# 'shell' command
>>> shell(f"du -hs foc/__init__.py 2>/dev/null") | fst | g_(_.split)()
['44K', 'foc/__init__.py']
# 'ls' command
>>> ls(".", r=True, grep="^(foc).*py$")
['foc/__init__.py']
# dot-accessible dict
>>> d = dmap(name="yunchan lim", age=19)
>>> d.cliburn.semifinal.concerto = "Mozart Piano Concerto No.22, K.482"
>>> d.cliburn.semifinal.recital = "Liszt 12 Transcendental Etudes"
>>> d.cliburn.final = "Rachmaninov Piano Concerto No.3, Op.30"
>>> d | nprint
cliburn | final | Rachmaninov Piano Concerto No.3, Op.30
: semifinal | concerto | Mozart Piano Concerto No.22, K.482
: : recital | Liszt 12 Transcendental Etudes
name | yunchan lim
# and more ...
```
> To see all the functions provided by `foc`,
```python
>>> catalog() | nprint
```
## Install
```bash
$ pip install -U foc
```
## What is `fx`?
> `fx` (_Function eXtension_) is the backbone of `foc` and provides a _new syntax_ when composing functions.
> `fx` basically maps every function in Python to a _monadic function_ in **`fx` monad**.
> More precisely, `fx` is a _lift function_, but here, I also refer to the functions generated by `fx` as `fx`.
### 1. **`fx` is a _composable_ function using symbols.**
There are two ways to compose functions with symbols as shown in _the previous section_.
| Symbol | Description | Evaluation Order | Same as in Haskell |
|:---------------------:|:-------------------------------------------------------:|:----------------:|:------------------:|
| **`.`** (_dot_) | Same as dot(`.`) _mathematical symbol_ | _Right-to-Left_ | `(<=<)` |
| **`\|`** (_pipeline_) | In _Unix_ pipeline manner | _Left-to-Right_ | `(>=>)` |
| `fx` | _Lift function_. Convert functions into _monadic_ forms | - | `(pure .)` |
> _If you don't like function composition using symbols, use **`cf_`**._
> _In fact, it's the most reliable and safe way to use it for all functions._
### 2. **`fx` is really easy to make.**
`fx` is just a function decorated by `@fx`.
**Wrap any function in `fx`** when you need function composition on the fly.
```python
>>> [1, 2, 3] | sum | (lambda x: x * 7) # error, lambda is not a 'fx'
TypeError: unsupported operand ...
>>> [1, 2, 3] | sum | fx(lambda x: x * 7) # just wrap it in 'fx'.
42
>>> @fx
... def func(arg): # place @fx above the definition or bind 'g = fx(func)'
... ... # 'func' is now 'composable' with symbols
```
> Most of the functions provided by `foc` are `fx` functions.
> If you don't have one, you can just create one and use it.
### 3. **`fx` is a curried function.**
```python
# currying 'map' -> map(predicate)(iterable)
>>> map(_ * 8)(seq(1,...)) | takel(5) # seq(1,...) == [1,2,3,..], 'infinite' sequence
[8, 16, 24, 32, 40]
# bimap := bimap(f, g, tuple), map over both 'first' and 'second' argument
>>> bimap(_ + 3)(_ * 7)((5, 7))
(8, 49)
>>> foldl(op.sub)(10)(range(1, 5))
0
>>> @fx
... def args(a, b, c, d):
... return f"{a}-{b}-{c}-{d}"
>>> args(1)(2)(3)(4) == args(1,2)(3,4) == args(1,2,3)(4) == args(1)(2,3,4) == args(1,2,3,4)
True
```
> You can get the curried function of `g` with `fx(g)`.
> But if you want to get a curried function other than `fx`, use `curry(g)`.
### 4. **_Lambdas_ with `_` are `fx`.**
```python
>>> [1, 2, 3] | sum | (_ * 7) # Use '_' lambda instead.
42
>>> ((_ * 6) . fx(_ + 4))(3) # (3 + 4) * 6
42
>>> 2 | (_ * 7) | (60 % _) | (_ // 3) # (60 % (2 * 7)) // 3
1
```
**Partial application** driven by `_` is also possible when accessing `dict`, `object` or `iterable`, or even _calling functions_.
| Operator | Equivalent Function |
|----------------|--------------------------|
| `_[_]` | `op.getitem` |
| `_[item]` | `op.itemgetter(item)` |
| `_._` | `getattr` |
| `_.attr` | `op.attrgetter(attr)` |
| ``_(_)`` | ``apply`` |
| ``_(*a, **k)`` | ``lambda f: f(*a, **k)`` |
```python
# dict
>>> d = dict(one=1, two=2, three="three")
>>> _[_](d)("two") # curry(lambda a, b: a[b])(d)("two")
2
>>> _["one"](d) # (lambda x: x["one"])(d)
1
>>> cf_(_[2:4], _["three"])(d) # d["three"][2:4]
're'
# iterable
>>> r = range(5)
>>> _[_](r)(3) # curry(lambda a, b: a[b])(r)(3)
3
>>> _[3](r) # (lambda x: x[3])(r)
3
# object
>>> o = type('', (), {"one": 1, "two": 2, "three": "three"})()
>>> _._(o)("two") # curry(lambda a, b: getattr(a, b))(o)("two")
2
>>> _.one(o) # (lambda x: x.one)(o)
1
>>> o | _.three | _[2:4] # o.three[2:4]
're'
# function caller
>>> _(_)(foldl)(op.add)(0)(range(5))
10
>>> _(7 * _)(mapl)(range(1, 10))
[7, 14, 21, 28, 35, 42, 49, 56, 63]
# Not seriously, this creates multiplication table.
>>> [ mapl(f)(range(1, 10)) for f in _(_ * _)(map)(range(1, 10)) ]
```
## Don't forget that `foc` is a collection, _albeit very odd_.
[Everything in one place](https://github.com/thyeem/foc/blob/main/foc/__init__.py).
- `fx` _pure basic functions_ `id`, `const`, `take`, `drop`, `repeat`, `replicate`..
- _higher-order_ functions like `cf_`, `f_`, `g_`, `curry`, `uncurry`, `map`, `filter`, `zip`,..
- useful yet very fundamental like `seq`, `force`, `trap`, `error`, `guard`,..
## Real-World Example
A _causal self-attention_ of the `transformer` model based on `pytorch` can be described as follows.
_Some_ claim that this helps follow the workflow of tensor operation without distraction. (_plus, 3-5% speed-up_)
```python
def forward(self, x):
B, S, E = x.size() # size_batch, size_block (sequence length), size_embed
N, H = self.config.num_heads, E // self.config.num_heads # E == (N * H)
q, k, v = self.c_attn(x).split(self.config.size_embed, dim=2)
q = q.view(B, S, N, H).transpose(1, 2) # (B, N, S, H)
k = k.view(B, S, N, H).transpose(1, 2) # (B, N, S, H)
v = v.view(B, S, N, H).transpose(1, 2) # (B, N, S, H)
# Attention(Q, K, V)
# = softmax( Q*K^T / sqrt(d_k) ) * V
# // q*k^T: (B, N, S, H) x (B, N, H, S) -> (B, N, S, S)
# = attention-prob-matrix * V
# // prob @ v: (B, N, S, S) x (B, N, S, H) -> (B, N, S, H)
# = attention-weighted value (attention score)
return cf_(
self.dropout, # dropout of layer's output
self.c_proj, # linear projection
g_(_.view)(B, S, E), # (B, S, N, H) -> (B, S, E)
torch.Tensor.contiguous, # contiguos in-memory tensor
g_(_.transpose)(1, 2), # (B, S, N, H)
_ @ v, # (B, N, S, S) x (B, N, S, H) -> (B, N, S, H)
self.dropout_attn, # attention dropout
f_(F.softmax, dim=-1), # softmax
g_(_.masked_fill)(mask == 0, float("-inf")), # no-look-ahead
_ / math.sqrt(k.size(-1)), # / sqrt(d_k)
_ @ k.transpose(-2, -1), # Q @ K^T -> (B, N, S, S)
)(q)
```
Raw data
{
"_id": null,
"home_page": "https://github.com/thyeem/foc",
"name": "foc",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": null,
"keywords": "functional functools functional-python",
"author": "Francis Lim",
"author_email": "thyeem@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/56/90/b9943cf8a711660c01bbe6a9e280f3ce99240a0b1f030439ece3082debee/foc-0.6.1.tar.gz",
"platform": null,
"description": "<p align=\"center\"> <img src=\"https://raw.githubusercontent.com/thyeem/foc/main/foc.png\" height=\"180\"/></p>\n\n[![foc](https://img.shields.io/pypi/v/foc)](https://pypi.org/project/foc/)\n\n# foc\n\n_Func-Oriented Code_ or _Francis' Odd Collection_.\n\n`foc` is a _non-frilled_ and _seamlessly integrated_ functional `Python` tool.\n\n\n- provides a collection of ___higher-order functions___ and ___placeholder lambda syntax___ (`_`)\n- provides an easy way to ___compose functions with symbols___. (`.` and `|`)\n\n```python\n>>> from foc import *\n\n# in standard Python, we normally use, \n>>> sum(map(lambda x: x+1, range(10))) \n55\n\n# 'foc' allows three more things:\n>>> cf_(sum, map(_ + 1))(range(10)) # using the 'cf_' compose function\n55\n\n>>> (sum . map(_ + 1))(range(10)) # using '.' mathematical symbol (:P)\n55\n\n>>> range(10) | map(_ + 1) | sum # using '|' Unix pipeline style\n55\n\n# Scala-style placeholder syntax lambda expression \n>>> (_ + 7)(3) # same as (lambda x: x + 7)(3)\n10\n\n# (3 + 4) * 6\n>>> cf_(_ * 6, _ + 4)(3) # function.\n42 \n \n>>> 3 | _ + 4 | _ * 6 # pipeline.\n42 \n \n>>> ((_ * 6) . fx(_ + 4))(3) # dot. Wrap 'lambda expression' in 'fx' when using '.'.\n42\n```\n\nRemember that it is only necessary to use `fx` _when using lambda expressions and `.`_. \nThat's all. \n\nFor more examples, see the [documentation](https://github.com/thyeem/foc/blob/main/foc/__init__.py#L150) provided with each function.\n\n```python\n>>> (rev . filter(even) . range)(10) # list(reversed(filter(even, range(10))))\n[8, 6, 4, 2, 0]\n\n>>> ((_ * 5) . nth(3) . range)(5) # range(5)[3] * 5\n10\n\n>>> (collect . filter(_ == \"f\"))(\"fun-on-functions\") # list(filter(lambda x: x == \"f\", \"fun-on-functions\"))\n['f', 'f']\n\n# To use built-ins 'list' on the fly, \n>>> (fx(list) . map(abs) . range)(-2, 3) # list(map(abs, range(-2, 3)))\n[2, 1, 0, 1, 2]\n\n>>> range(73, 82) | map(chr) | unchars # unchars(map(chr, range(73, 82)))\n'IJKLMNOPQ'\n```\n\n## _Ouch_\n[`ouch`](https://github.com/thyeem/ouch) is a collection of utilities that are based on and aligned with `foc`.\n\n```python\nfrom ouch import *\n\n# soft/hard flatten\n>>> flatten([1, [2, 3, (4, 5)]])\n[1, 2, 3, (4, 5)]\n>>> [1, [(2,), [[{3}, (x for x in range(3))]]]] | flat | collect\n[1, 2, 3, 0, 1, 2]\n\n# 'shell' command\n>>> shell(f\"du -hs foc/__init__.py 2>/dev/null\") | fst | g_(_.split)()\n['44K', 'foc/__init__.py']\n\n# 'ls' command\n>>> ls(\".\", r=True, grep=\"^(foc).*py$\")\n['foc/__init__.py']\n\n# dot-accessible dict\n>>> d = dmap(name=\"yunchan lim\", age=19)\n>>> d.cliburn.semifinal.concerto = \"Mozart Piano Concerto No.22, K.482\"\n>>> d.cliburn.semifinal.recital = \"Liszt 12 Transcendental Etudes\"\n>>> d.cliburn.final = \"Rachmaninov Piano Concerto No.3, Op.30\"\n>>> d | nprint\n cliburn | final | Rachmaninov Piano Concerto No.3, Op.30\n : semifinal | concerto | Mozart Piano Concerto No.22, K.482\n : : recital | Liszt 12 Transcendental Etudes\n name | yunchan lim\n \n# and more ...\n```\n\n> To see all the functions provided by `foc`, \n```python\n>>> catalog() | nprint\n```\n\n## Install\n```bash\n$ pip install -U foc\n```\n\n## What is `fx`?\n> `fx` (_Function eXtension_) is the backbone of `foc` and provides a _new syntax_ when composing functions. \n> `fx` basically maps every function in Python to a _monadic function_ in **`fx` monad**. \n> More precisely, `fx` is a _lift function_, but here, I also refer to the functions generated by `fx` as `fx`.\n\n### 1. **`fx` is a _composable_ function using symbols.**\n\nThere are two ways to compose functions with symbols as shown in _the previous section_.\n| Symbol | Description | Evaluation Order | Same as in Haskell |\n|:---------------------:|:-------------------------------------------------------:|:----------------:|:------------------:|\n| **`.`** (_dot_) | Same as dot(`.`) _mathematical symbol_ | _Right-to-Left_ | `(<=<)` |\n| **`\\|`** (_pipeline_) | In _Unix_ pipeline manner | _Left-to-Right_ | `(>=>)` |\n| `fx` | _Lift function_. Convert functions into _monadic_ forms | - | `(pure .)` |\n\n> _If you don't like function composition using symbols, use **`cf_`**._ \n> _In fact, it's the most reliable and safe way to use it for all functions._\n\n### 2. **`fx` is really easy to make.**\n`fx` is just a function decorated by `@fx`. \n**Wrap any function in `fx`** when you need function composition on the fly.\n\n```python\n>>> [1, 2, 3] | sum | (lambda x: x * 7) # error, lambda is not a 'fx'\nTypeError: unsupported operand ...\n\n>>> [1, 2, 3] | sum | fx(lambda x: x * 7) # just wrap it in 'fx'.\n42\n\n>>> @fx\n... def func(arg): # place @fx above the definition or bind 'g = fx(func)'\n... ... # 'func' is now 'composable' with symbols\n```\n> Most of the functions provided by `foc` are `fx` functions. \n> If you don't have one, you can just create one and use it.\n\n### 3. **`fx` is a curried function.**\n\n```python\n# currying 'map' -> map(predicate)(iterable)\n>>> map(_ * 8)(seq(1,...)) | takel(5) # seq(1,...) == [1,2,3,..], 'infinite' sequence\n[8, 16, 24, 32, 40] \n\n# bimap := bimap(f, g, tuple), map over both 'first' and 'second' argument\n>>> bimap(_ + 3)(_ * 7)((5, 7))\n(8, 49)\n>>> foldl(op.sub)(10)(range(1, 5))\n0\n>>> @fx\n... def args(a, b, c, d):\n... return f\"{a}-{b}-{c}-{d}\"\n>>> args(1)(2)(3)(4) == args(1,2)(3,4) == args(1,2,3)(4) == args(1)(2,3,4) == args(1,2,3,4)\nTrue\n```\n> You can get the curried function of `g` with `fx(g)`. \n> But if you want to get a curried function other than `fx`, use `curry(g)`. \n\n### 4. **_Lambdas_ with `_` are `fx`.**\n\n```python\n>>> [1, 2, 3] | sum | (_ * 7) # Use '_' lambda instead.\n42\n>>> ((_ * 6) . fx(_ + 4))(3) # (3 + 4) * 6\n42\n>>> 2 | (_ * 7) | (60 % _) | (_ // 3) # (60 % (2 * 7)) // 3\n1\n```\n\n**Partial application** driven by `_` is also possible when accessing `dict`, `object` or `iterable`, or even _calling functions_. \n\n| Operator | Equivalent Function |\n|----------------|--------------------------|\n| `_[_]` | `op.getitem` |\n| `_[item]` | `op.itemgetter(item)` |\n| `_._` | `getattr` |\n| `_.attr` | `op.attrgetter(attr)` |\n| ``_(_)`` | ``apply`` |\n| ``_(*a, **k)`` | ``lambda f: f(*a, **k)`` |\n\n\n```python\n# dict\n>>> d = dict(one=1, two=2, three=\"three\")\n>>> _[_](d)(\"two\") # curry(lambda a, b: a[b])(d)(\"two\")\n2\n>>> _[\"one\"](d) # (lambda x: x[\"one\"])(d)\n1\n>>> cf_(_[2:4], _[\"three\"])(d) # d[\"three\"][2:4]\n're'\n\n# iterable\n>>> r = range(5)\n>>> _[_](r)(3) # curry(lambda a, b: a[b])(r)(3)\n3\n>>> _[3](r) # (lambda x: x[3])(r)\n3\n\n# object\n>>> o = type('', (), {\"one\": 1, \"two\": 2, \"three\": \"three\"})()\n>>> _._(o)(\"two\") # curry(lambda a, b: getattr(a, b))(o)(\"two\")\n2\n>>> _.one(o) # (lambda x: x.one)(o)\n1\n>>> o | _.three | _[2:4] # o.three[2:4]\n're'\n\n# function caller \n>>> _(_)(foldl)(op.add)(0)(range(5))\n10\n>>> _(7 * _)(mapl)(range(1, 10))\n[7, 14, 21, 28, 35, 42, 49, 56, 63]\n\n# Not seriously, this creates multiplication table.\n>>> [ mapl(f)(range(1, 10)) for f in _(_ * _)(map)(range(1, 10)) ]\n```\n\n## Don't forget that `foc` is a collection, _albeit very odd_.\n\n[Everything in one place](https://github.com/thyeem/foc/blob/main/foc/__init__.py).\n\n- `fx` _pure basic functions_ `id`, `const`, `take`, `drop`, `repeat`, `replicate`..\n- _higher-order_ functions like `cf_`, `f_`, `g_`, `curry`, `uncurry`, `map`, `filter`, `zip`,.. \n- useful yet very fundamental like `seq`, `force`, `trap`, `error`, `guard`,..\n\n## Real-World Example\nA _causal self-attention_ of the `transformer` model based on `pytorch` can be described as follows. \n_Some_ claim that this helps follow the workflow of tensor operation without distraction. (_plus, 3-5% speed-up_)\n\n```python\n def forward(self, x):\n B, S, E = x.size() # size_batch, size_block (sequence length), size_embed\n N, H = self.config.num_heads, E // self.config.num_heads # E == (N * H)\n\n q, k, v = self.c_attn(x).split(self.config.size_embed, dim=2)\n q = q.view(B, S, N, H).transpose(1, 2) # (B, N, S, H)\n k = k.view(B, S, N, H).transpose(1, 2) # (B, N, S, H)\n v = v.view(B, S, N, H).transpose(1, 2) # (B, N, S, H)\n\n # Attention(Q, K, V)\n # = softmax( Q*K^T / sqrt(d_k) ) * V\n # // q*k^T: (B, N, S, H) x (B, N, H, S) -> (B, N, S, S)\n # = attention-prob-matrix * V\n # // prob @ v: (B, N, S, S) x (B, N, S, H) -> (B, N, S, H)\n # = attention-weighted value (attention score)\n\n return cf_(\n self.dropout, # dropout of layer's output\n self.c_proj, # linear projection\n g_(_.view)(B, S, E), # (B, S, N, H) -> (B, S, E)\n torch.Tensor.contiguous, # contiguos in-memory tensor\n g_(_.transpose)(1, 2), # (B, S, N, H)\n _ @ v, # (B, N, S, S) x (B, N, S, H) -> (B, N, S, H)\n self.dropout_attn, # attention dropout\n f_(F.softmax, dim=-1), # softmax\n g_(_.masked_fill)(mask == 0, float(\"-inf\")), # no-look-ahead\n _ / math.sqrt(k.size(-1)), # / sqrt(d_k)\n _ @ k.transpose(-2, -1), # Q @ K^T -> (B, N, S, S)\n )(q)\n```\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A collection of python functions for somebody's sanity",
"version": "0.6.1",
"project_urls": {
"Homepage": "https://github.com/thyeem/foc"
},
"split_keywords": [
"functional",
"functools",
"functional-python"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d70133840dbabd7b246cfb71f6b07f49a24c685d0338e5346e3c25a78fcdc1d4",
"md5": "add43aaf320a8d106e881556059d802a",
"sha256": "ded679d2729b5f75aafac9737aceebb4c45db229dd5bb86a877a5c760d640a9d"
},
"downloads": -1,
"filename": "foc-0.6.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "add43aaf320a8d106e881556059d802a",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 19056,
"upload_time": "2024-11-19T23:49:18",
"upload_time_iso_8601": "2024-11-19T23:49:18.711212Z",
"url": "https://files.pythonhosted.org/packages/d7/01/33840dbabd7b246cfb71f6b07f49a24c685d0338e5346e3c25a78fcdc1d4/foc-0.6.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "5690b9943cf8a711660c01bbe6a9e280f3ce99240a0b1f030439ece3082debee",
"md5": "7f5d347314dc72e8c9fe14e2e15e69ab",
"sha256": "9799d092053d2238cc607839ba0dbd00433dcdf3b37c8816c0bee71a034089df"
},
"downloads": -1,
"filename": "foc-0.6.1.tar.gz",
"has_sig": false,
"md5_digest": "7f5d347314dc72e8c9fe14e2e15e69ab",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 24116,
"upload_time": "2024-11-19T23:49:21",
"upload_time_iso_8601": "2024-11-19T23:49:21.330918Z",
"url": "https://files.pythonhosted.org/packages/56/90/b9943cf8a711660c01bbe6a9e280f3ce99240a0b1f030439ece3082debee/foc-0.6.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-19 23:49:21",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "thyeem",
"github_project": "foc",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"lcname": "foc"
}