{ "info": { "author": "Erich Focht", "author_email": "efocht@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Education", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License (GPL)", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# PyVEO: Python bindings to VEO\n\nThis package provides python bindings to VEO: Vector Engine Offloading.\n\n## Introduction\n\nThe NEC Aurora Tsubasa Vector Engine (VE) is a very high memory\nbandwidth vector processor with HBM2 memory in the form-factor of a\nPCIe card. Currently up to eight VE cards can be inserted into a\nvector host (VH) which is typically a x86_64 server.\n\nThe primary usage model of the VE is as a standalone computer which\nuses the VH for offloading its operating system functionality. Each VE\ncard behaves like a separate computer with its own instance of\noperating system (VEOS), it runs native VE programs compiled for the\nvector CPU that are able to communicate with other VEs through MPI.\n\nA second usage model of VEs lets native VE programs offload\nfunctionality to the VH with the help of the VHcall mechanisms. The VH\nis used by the VE as an accelerator for functions it is better suited\nfor, like unvectorizable code.\n\nThe third usage model is the classical accelerator model with a main\nprogram compiled for the VH running high speed program kernels on the\nVE. A mechanism for this usage model is the VE Offloading (VEO)\nlibrary provided by the veofload and veoffload-veorun RPMs.\n\nThis Python module is an implementation of the VEO API for Python\nprograms. It is an extension to the C API and exposes the mechanisms\nthrough Python objects.\n\n\n## Python VEO API\n\n**Overview**\n\n![PyVEO components](doc/pyveo_components2.png)\n\nThe Python classes are depicted as boxes (VeoProc, VeoLibrary,\nVeBuild, ...), some of their methods are labeling arrows that lead to\nnew classes. The following sections document the classes, their\nmethods and attributes.\n\n\n### VeoProc\n\nA `VeoProc` object corresponds to one running instance of the `veorun`\nVE program that controls one address space on the VE. The command\n```python\nfrom veo import *\n\nproc = VeoProc(nodeid)\n```\ncreates a VEO process instance on the VE node `nodeid`. By default `VeoProc()`\nstarts `/opt/nec/ve/libexec/veorun`. It can be replaced by an own version with\nstatically linked libraries by pointing the environment variable **VEORUN_BIN**\nto it.\n\nStarting with VEO version 1.3.2 a statically linked *veorun* can also be specified\nas an argument when calling *veo_proc_create_static()*. This new VEO API call\nwas implemented into the *VepProc* *__init()* method, which can now take the\nstatically compiled veorun binaries path as an additional argument:\n```python\nfrom veo import *\n\nproc = VeoProc(nodeid, veorun_bin_path)\n```\nThis change is available at and after the **v1.3.3** tag of *py-veo*.\n\n\n**Methods:**\n- `load_library(libname)` loads a `.so` dynamically linked shared object\nfileinto the VEOProc address space. It returns a `VeoLibrary` object.\n- `static_library()` returns a `VeoLibrary` object exposing the symbols\nand functions statically linked with the running `veorun`-instance of\nthis `VeoProc`.\n- `alloc_mem(size_t size)` allocates a memory buffer of size *size* on\nthe VE and returns a `VEMemPtr` object that points to it.\n- `free_mem(VEMemPtr memptr)` frees the VE memory pointed to by the\n`VEMemPtr` argument.\n- `read_mem(dst, VEMemPtr src, size_t size)` read memory from\nthe VE memory buffer that *src* points to into the *dst* object transfering\n*size* bytes. The *dst* python object must support the buffer protocol.\n- `write_mem(VEMemPtr dst, src, size_t size)` write *size* bytes\nfrom the *src* object to the VE memory buffer pointed to by the *dst*\nVEMemPtr. The *src* object must support the buffer protocol.\n- `open_context()` opens a worker thread context on the VE.\n- `close_context(VeoContext ctx)` closes a context on the VE.\n- `get_function(name)` searches for the function *name* in the `VeoFunction`\ncache of each `VeoLibrary` object of the current `VeoProc` and returns the\n`VeoFunction` object. A VE function appears in a library's cache only if it\nwas looked up before with the `find_library()` method of the `VeoLibrary` object.\n\n**Attributes:**\n- `nodeid` is the VE node ID on which the `VeoProc` is running.\n- `context` is a list with the contexts active in the current `VeoProc` instance.\n- `lib` is a dict of the `VeoLibrary` objects loaded into the `VeoProc`.\n\n\n### VeoCtxt\n\nVE Offloading thread context that corresponds to one VE worker\nthread. Technically it is cloned from the control thread started\nby the VeoProc therefore all VeoCtxt instances share the same\nmemory and are controlled by their parent *VeoProc*.\n\nEach VE context has two queues, a command queue and a completion\nqueue. Calling an offloaded VE function creates a request on the\ncommand queue, when the request is finished the result is added to the\ncompletion queue.\n\n**Methods:**\n- `async_read_mem(dst, VEMemPtr src, size_t size)` queue a request to\nread memory from the VE memory buffer that *src* points to into the\n*dst* object transfering *size* bytes. The *dst* python object must\nsupport the buffer protocol.\n- `async_write_mem(VEMemPtr dst, src, size_t size)` queue a request to\nwrite *size* bytes from the *src* object to the VE memory buffer\npointed to by the *dst* VEMemPtr. The *src* object must support the\nbuffer protocol.\n\n**Attributes:**\n- `proc`: the *VeoProc* to which the context belongs.\n\nTODO: expose the PID/TID of a VeoCtxt such that we can pin it to certain cores.\n\n\n### VeoLibrary\n\nFunctions that need to be called on the VE must be loaded into the\n*VeoProc* by loading a shared library .so file into the process\nrunning on the VE. This is done by calling the `load_library()` method\nof the *VeoProc* instance. The result is an instance of the\n*VeoLibrary* class.\n\nExample:\n```python\nimport os\n\nlib = proc.load_library(os.getcwd() + \"/libvetest.so\")\n```\n\nA special instance of *VeoLibrary* is the \"static\" library, that\nrepresents the functions and symbols statically linked with the\n*veorun* VE program that has been started by the *VeoProc*\ninstance. It does not need to be loaded but can be accessed by the\nmethod `static_library()`.\n```python\nslib = proc.static_library()\n```\n\nThe static library feature only needs to be used when the offloaded\nfunctions can not be linked dynamically or cannot be compiled with\n`-fpic`, for example because some of the libraries it uses is not\navailable as dynamic library.\n\n**Methods:**\n- `get_symbol(name)`: find a symbol's address in the *VeoLibrary* and return it as a *VEMemPtr*.\n- `find_function(name)`: find a function in the current library and return it as an instance of *VeoFunction*.\n\nUnknown attributes of a *VeoLibrary* object are treated like functions\nthat are implicitly searched with the *find_function()*. The search is\nonly done once and the *VeoFunction* object is cached inside the\nobject in the *func* dict (see below). If the function is not found an\nexception will be raised. This means that a function *foo* inside a\nlibrary object *lib* can be simply addressed as `lib.foo`.\n\n\n**Attributes:**\n- `name`: the name of the library, actually the full path from which it was loaded. The \"static\" library has the name `__static__`.\n- `proc`: the *VeoProc* instance to which the library belongs.\n- `func`: a `dict` containing all functions that were 'found' in the current library. The values are the corresponding *VeoFunction* instances.\n- `symbol`: a `dict` containing all symbols and their *VEMemPtr* that were searched and found in the current library.\n\n\n### VeoFunction\n\nOffloaded functions located inside *VeoLibrary* objects are\nrepresented by instances of the *VeoFunction* class. This object\nlogically \"belongs\" to the *VeoLibrary* in which the function was\nlocated by calling the `find_function()` method. The object contains\nthe address of the function in the VE address space of the *VeoProc*\nprocess. If you have multiple processes that you use (for example\nbecause you use multiple VE cards on the same hosts), the function\nneeds to be located in each of them, and you will need to handle\nmultiple instances of *VeoFunction*, one for each *VeoProc*.\n\nOnce \"found\" in a library, the *VeoFunction* instance is added to the\n`func` dict of the *VeoLibrary* with the function name as key. The\nmethod `get_function()` of *VeoProc* can search the function name\ninside the *VeoLibrary* hashes of all libraries loaded into the\nprocess.\n\n**Methods:**\n- `args_type(*args)`: sets the data types for the arguments of the function. The arguments must contain strings describing the base data types: \"char\", \"short\", \"int\", \"long\", \"float\", \"double\", preceeded by \"unsigned\" if needed, ending with a \"*\" if the data types represent pointers. \"void *\" is a valid data type as an argument. Arrays are not allowed. Structs should not be passed by value, only by reference.\n- `ret_type(rettype)`: specify the data type of the return value as a string. Same restrictions as for arguments apply. \"void\" is a valid return type.\n- `__call__(VeoCtxt ctx, *args)`: the call method allows to asynchronously offload a function call to the VE. `ctx` specifies a *VeoContext* in which the function should be called, `*args` are the arguments of the function, corresponding to the prototype set with the `args_type()` method. The `__call__` method allows one to use an instance of the class as if it were a function. It returns a *VeoRequest* object.\n\n**Attributes:**\n- `lib`: the *VeoLibrary* object to which the function belongs.\n- `name`: the name of the function inside the VE process.\n- `_args_type`: the argument types string list.\n- `_ret_type`: the return value type string.\n\nThe *__call__* method supports a special kind of argument: an instance\nof the class *OnStack*. The object `OnStack(buff, size)` will result\nin the buffer *buff* of size *size* being copied over onto the VE\nstack and behave like a temporary variable of the calling\nfunction. The corresponding argument will point to the address on the\nstack. Currently only arguments with intent \"IN\" are supported,\ni.e. they should only be read by the callee. They are lost after the\nVE function finishes and are overwritten by the following VEO function\ncall.\n\n**Notes:**\n\nThe arguments to a function must fit into a 64 bit\nregister. It is possible to pass values (char, int, long, float,\ndouble) or pointers to memory locations inside the VE process. When\npassing something like a struct, the value of the struct must be\ntransfered to VE memory separately, before calling the function, and\nthe corresponding argument should point to that memory location.\n\nA maximum of 32 arguments to a function call are supported. When the\nnumber of arguments doesn't exceed 8, the arguments are passed in\nregisters. For more than 8 arguments the values are passed on stack.\n\nCalling a function is asynchronous. The function and its arguments are\nqueued in the command queue of the *VeoContext*.\n\n\n### OnStack\n\nWith *OnStack* it is possible to pass in and out arguments that need\nto be accessed by reference. Python objects that support the buffer\ninterface are supported as arguments of *OnStack*. The initialization\nsyntax is:\n```py\nOnStack(buff, [size=...], [inout=...])\n```\nwith the arguments:\n- `buff`: is a python object that supports the buffer interface and is contiguous in memory.\n- `size`: can limit the size of the transfer. If not specified, the size of the buffer is used.\n- `inout`: the scope of the transfer, can be `VEO_INTENT_IN`, `VEO_INTENT_OUT` or `VEO_INTENT_INOUT`.\n\nWith *VEO_INTENT_IN* the Python *buff* object's buffer is copied onto\nthe VE stack right before calling the VE kernel. With *VEO_INTENT_OUT*\nthe buffer is not copied in but copied out from the VE's stack into\nthe Python object's buffer after the VE kernel has finished\nexecution. *VEO_INTENT_INOUT* obviously copies data in before\nexecution and out after.\n\n\n### VeoRequest\n\nEach call to a *VeoFunction* returns a *VeoRequest* which helps track\nthe status of the offloaded function call and retrieve its result.\n\n**Methods:**\n- `wait_result()`: wait until the request has been completed. Returns the result, converted to the data type as specified with the *VeoFunction* `ret_type()` method. Raises an `ArithmeticError` if the function raised an exception, and a `RuntimeError` if the execution failed in another way.\n- `peek_result()`: immediately returns after checking whether the request was completed or not. If the request was completed, it returns the result, like `wait_result()`. If the command did not finish, yet, it returns a `NameError` exception. The other error cases are the same as for `wait_result()`.\n\n**Attributes:**\n- `req`: the internal request ID inside the *VeoCtxt* command queue.\n- `ctx`: the *VeoCtxt* context this request belongs to.\n\n\n### VEMemPtr\n\nA *VEMemPtr* object represents a pointer to a memory location on the\nVE, inside a *VeoProc* process. It can be created by allocating memory\ninside a *VeoProc* process, finding a symbol inside a *VeoLibrary*, or\nsimply instantiating a VEMemPtr when the VE address is known.\n\nExample:\n```python\nve_buff = proc.alloc_mem(10000)\n\ntable = lib.get_symbol(\"table_inside_library\")\n```\n\n**Attributes:**\n- `addr`: the memory location within the processes' VE virtual address space.\n- `size`: the size of the memory object. This is only know if the *VEMemPtr* was created by `alloc_mem()`. It is useful for debugging, has no function otherwise.\n- `proc`: the *VeoProc* instance to which the memory belongs.\n\n\n### VeBuild\n\nA `VeBuild` object provides simple wrapper functionality around\nSX-Aurora compilation and linking of VE code into either a dynamically\nshared object usable as a loadable *VeoLibrary*, or a statically\nlinked *veorun* that includes the VE kernels. It also allows to inline\nC, C++, Fortran code into the Python program, and compile it from\nwithin the Python program. This way interactive examples of using the\nVE for offloading can be completely contained within a Python program\neg. inside a Jupyter or iPython notebook.\n\nIt is necessary to store the VEO kernels on disk and load them from\nthere because VEO can not load kernels from memory, yet.\n\n`VeBuild` is very simple code that still has some flaws regarding the\nerror handling and returns little information when errors occur. It is\nreally meant for small experiments, not for serious code development.\n\n**Methods:**\n- `set_c_src(label, content, [flags=...], [compiler=...])`: set a C source code module labeled by *label*. The method accepts following arguments:\n - *label*: a string with a name for this source code block. The source code block's content will end up in a file called <*label*>.c.\n - *content*: a raw string with the C code for this source block.\n - *flags*: optional named parameter with override flags for the compilation of this source code block. Must contain `-fpic`!\n - *compiler*: optional named parameter which overrides the default *ncc* C compiler for this source code block.\n- `set_cpp_src(label, content, [flags], [compiler])`: same as *set_c_src()* for C++ code.\n- `set_ftn_src(label, content, [flags], [compiler])`: same as *set_c_src()* for Fortran code.\n- `set_build_dir(dirname)`: set the directory in which the source blocks will be copied into files, the object files will be compiled and the *.so* and *veorun* files will be stored.\n- `build_so([label], [flags=...], [libs=[...]], [linker=...], [verbose=True])`: build a dynamically shared object from all registered source code blocks. Each source code block will be compiled as a separate object file and they will be linked together. The method returns the name of the `.so` file, if successful, *None* otherwise. This can raise exceptions!\n - *label*: an optional name for the `.so` file. If not specified, the name will be set to that of the first source block's label.\n - *flags*: a string with override flags for linking the `.so` file.\n - *libs*: a Python array with further libraries of objects to be linked. The strings will be added to the linker command.\n - *linker*: a string overriding the linker that is detected by the build command.\n - *verbose*: a boolean activating verbose output of comilation commands and their output. The default value is *False*.\n- `build_veorun([label], [flags=...], [libs=[...]], [verbose=True])`: build a *veorun* executable from the registered source code blocks. This executable can be used to create a *VeoProc* instance. The options are identical to those of *build_so()*. The method uses the *mk_veorun_static* command from the *veoffload-veorun* package. The command returns the name of the *veorun* executable if successful.\n- `clean()`: remove the source code and object files which were written during the compilation. The *.so* and *veorun* files are not deleted.\n- `clear()`: remove the internally stored source code blocks.\n- `realclean()`: calls the *clean()* method and removes all written *.so* and *veorun* files. Also remove the build directories that were created. A call to *realclean()* followed by a call to *clear()* initializes the *VeBuild* object and removes most of the things it created.\n\nA source code block can be replaced or updated by calling the\n*set_XYZ_src()* method again with the same label.\n\nWhen building a shared object or a statically linked *veorun* file the\nsource code blocks will be written into source files named after their\nlabels, in the current working directory. Make sure you don't\noverwrite anything! These source files are compiled into objects files\n(also in the current directory) and linked together into the `.so`\n or the *veorun* file.\n\n**NOTE:** When using the tripple quotes \"\"\", always prepend them by\n'r' (r\"\"\") such that the content is interpreted as raw\nstring. Otherwise the escaped characters will be interpreted and spoil\nthe source code.\n\nExample:\n```python\nfrom veo import *\n\nbld = VeBuild()\n\n# first c source module is a function summing up a vector\nbld.set_c_source(\"_test\", r\"\"\"\ndouble mysum(double *a, int n)\n{\n int i; double sum;\n for (i = 0; i < n; i++)\n sum += a[i];\n return sum;\n}\n\"\"\")\n\n# build the _test.so library in the current directory\nveo_name = bld.build_so(verbose=True)\n\n# remove temporary source and object code, keep the .so file\nbld.clean()\n\n# and now the VEO part\np = VeoProc(0)\nlib = p.load_library(os.getcwd() + \"/\" + veo_name)\nlib.mysum.args_type(\"double *\", \"int\")\nlib.mysum.ret_type(\"double\")\n\n```\n\n\n\n### Hooks\n\nWhenever a *VeoProc* object is created it will check for the existence\nof init hooks and call them at the end of the initialisation of the\n*VeoProc* object. Functions that are registered and called as an init\nhook must take one single argument: the *VeoProc* object. The are\nregistered by calling *set_proc_init_hook()*:\n```python\nfrom veo import set_proc_init_hook\n\ndef init_function(proc):\n # do something that needs to be done automatically\n # for each proc instance\n #...\n\nset_proc_init_hook(init_function)\n```\n\nA practical use for the init hooks is the registration of the VE BLAS functions in *py-vecblas*:\n```python\nfrom veo import set_proc_init_hook\n\ndef _init_cblas_funcs(p):\n lib = p.static_library()\n for k, v in _cblas_proto.items():\n f = lib.find_function(k)\n if f is not None:\n fargs = v[\"args\"]\n f.args_type(*fargs)\n f.ret_type(v[\"ret\"])\n\nset_proc_init_hook(_init_cblas_funcs)\n```\n\nThe registration of the VE BLAS functions needs to be done for every\ninstance of *VeoProc* because each of the instances must find and\nregister its own set of *VeoFunction*s. By registering the init hook\nthe user will not need to load a library and find a function for each\nof the started *VeoProc* processes, i.e. for each of the VE cards in\nthe system.\n\n\n## Build & Install\n\nThe easiest way to install is from PYPI / The Cheese Factory:\n```sh\npip install --upgrade py-veo\n```\n\nPrebuilt RPM packages are normally published in the github repository\n[releases](https://github.com/SX-Aurora/py-veo/releases).\n\nBulding from GIT requires *cython* and *numpy*. I prefer to do it from\ninside a virtualenv, but this is a matter of taste. Inside a\nvirtualenv only build the SRPM, do build the RPMs outside, otherwise\nthe paths to Python will be messed up and point inside the virtualenv.\n\nClone the repository from github:\n```sh\ngit clone https://github.com/SX-Aurora/py-veo.git\ncd py-veo\n```\n\nFor a quick test:\n```sh\nmake\n\n# try the examples\ncd examples\nmake\n```\n\nFor building RPMs:\n```sh\nmake srpm\n\n# this step needs to be done outside a virtualenv!\nrpmbuild --rebuild *.src.rpm\n```", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/sx-aurora/py-veo", "keywords": "", "license": "GPLv2", "maintainer": "", "maintainer_email": "", "name": "py-veo", "package_url": "https://pypi.org/project/py-veo/", "platform": "", "project_url": "https://pypi.org/project/py-veo/", "project_urls": { "Homepage": "https://github.com/sx-aurora/py-veo" }, "release_url": "https://pypi.org/project/py-veo/1.3.10/", "requires_dist": null, "requires_python": "", "summary": "Python bindings for the VE Offloading API", "version": "1.3.10" }, "last_serial": 4941504, "releases": { "1.3.10": [ { "comment_text": "", "digests": { "md5": "b98a18318edacc0b88952c519af9bf3b", "sha256": "8bbfb36b6e4d8007e33d05f83f62c5f0b20c3496842a72317e149cc9b0e787be" }, "downloads": -1, "filename": "py-veo-1.3.10.tar.gz", "has_sig": false, "md5_digest": "b98a18318edacc0b88952c519af9bf3b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 167351, "upload_time": "2019-03-14T20:38:08", "url": "https://files.pythonhosted.org/packages/10/43/7330962a7329c20d2719948da46b67804bfd534f796d9355af34b051a80a/py-veo-1.3.10.tar.gz" } ], "1.3.2": [ { "comment_text": "", "digests": { "md5": "0a162d4fa917fa5ee2ab87c08fe4abdc", "sha256": "b4321bd30ae52d082369904361d906cf9a54a3b54774b8b94d32cdb63474069c" }, "downloads": -1, "filename": "py-veo-1.3.2.tar.gz", "has_sig": false, "md5_digest": "0a162d4fa917fa5ee2ab87c08fe4abdc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 158414, "upload_time": "2018-12-09T01:07:19", "url": "https://files.pythonhosted.org/packages/0e/41/b820e42f6cd2fad03c49c9e40e81f4a671520f1c79ebfe4bf16c4fc6b860/py-veo-1.3.2.tar.gz" } ], "1.3.3": [ { "comment_text": "", "digests": { "md5": "620e5f97cb387b03e6d8af68552401a7", "sha256": "96183dd136bf0462fe1c827d4c62c8c40bbd4ff67178b4f447b3346a8866bc2c" }, "downloads": -1, "filename": "py-veo-1.3.3.tar.gz", "has_sig": false, "md5_digest": "620e5f97cb387b03e6d8af68552401a7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 160077, "upload_time": "2018-12-17T00:03:30", "url": "https://files.pythonhosted.org/packages/2f/08/760c6b07694c316213f9787bb83db10db479e34a9afc0fb95477e20c2b2b/py-veo-1.3.3.tar.gz" } ], "1.3.4": [ { "comment_text": "", "digests": { "md5": "2479b193946e9a8b4be037fa474b3f95", "sha256": "5a47e50c9d460d7f069c417120941d565efbe6e768e57df66c69a75ebe0a7084" }, "downloads": -1, "filename": "py-veo-1.3.4.tar.gz", "has_sig": false, "md5_digest": "2479b193946e9a8b4be037fa474b3f95", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 160080, "upload_time": "2018-12-17T00:08:51", "url": "https://files.pythonhosted.org/packages/10/25/5f55bac7737b21f45919e2608a022b19bd12f15ba7d4e108a5251739f136/py-veo-1.3.4.tar.gz" } ], "1.3.8": [ { "comment_text": "", "digests": { "md5": "89233482add808cc497d022eb2122b00", "sha256": "c1aa32de243224e36236ea7195bd56863ed5e7829ae210bac35eabb3800d5925" }, "downloads": -1, "filename": "py-veo-1.3.8.tar.gz", "has_sig": false, "md5_digest": "89233482add808cc497d022eb2122b00", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 561925, "upload_time": "2018-12-19T15:06:34", "url": "https://files.pythonhosted.org/packages/83/0e/01e30fe43274d9af5c3fcc5cf90c40339608657fa2bc56240e24ddd5bdd9/py-veo-1.3.8.tar.gz" } ], "1.3.9": [ { "comment_text": "", "digests": { "md5": "d0768d5b8547cce653e02f245798ebf3", "sha256": "33147bcefcfdd864a889407708ef7d3909bb6398d8452a57611d44b642710f8e" }, "downloads": -1, "filename": "py-veo-1.3.9.tar.gz", "has_sig": false, "md5_digest": "d0768d5b8547cce653e02f245798ebf3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 164417, "upload_time": "2019-03-07T00:25:23", "url": "https://files.pythonhosted.org/packages/87/a7/1e562402d653a50ec764008ebd09f9367ff3ea1f77ba79ac58f63ef47223/py-veo-1.3.9.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b98a18318edacc0b88952c519af9bf3b", "sha256": "8bbfb36b6e4d8007e33d05f83f62c5f0b20c3496842a72317e149cc9b0e787be" }, "downloads": -1, "filename": "py-veo-1.3.10.tar.gz", "has_sig": false, "md5_digest": "b98a18318edacc0b88952c519af9bf3b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 167351, "upload_time": "2019-03-14T20:38:08", "url": "https://files.pythonhosted.org/packages/10/43/7330962a7329c20d2719948da46b67804bfd534f796d9355af34b051a80a/py-veo-1.3.10.tar.gz" } ] }