Metadata-Version: 2.1
Name: setuptools-zig
Version: 0.1.3
Summary: A setuptools extension, for building cpython extensions with Zig.
Home-page: https://sourceforge.net/p/setuptools-zig/code/ref/default/
Author-email: a.van.der.neut@ruamel.eu
License: MIT
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: Implementation :: CPython
Requires-Python: >=3.6.5
Description-Content-Type: text/x-rst


Setuptools-Zig
==============

.. image:: https://sourceforge.net/p/setuptools-zig/code/ci/default/tree/_doc/_static/license.svg?format=raw
   :target: https://opensource.org/licenses/MIT

.. image:: https://sourceforge.net/p/setuptools-zig/code/ci/default/tree/_doc/_static/pypi.svg?format=raw
   :target: https://pypi.org/project/setuptools-zig/

.. image:: https://sourceforge.net/p/oitnb/code/ci/default/tree/_doc/_static/oitnb.svg?format=raw
   :target: https://bitbucket.org/ruamel/oitnb/


A setuptools extension for building cpython extensions written in Zig and/or C with the `Zig compiler <https://ziglang.org>`_.

This extension expects to find the ``zig`` command in your ``PATH``. If it is not
there, or if you need to select a specific version, you can set the environment
variable ``PY_ZIG`` to the full path of the executable. E.g.::

   PY_VER=/usr/local/bin/zig

This module has been developed with Zig 0.7.0, but should work with other versions (as long as you adapt your Zig code).


The package ``setuptools-zig`` is available on PyPI, but doesn't need to be
installed as it is a setup requirement. Once your ``setup.py`` has the apropriate
entries, building an ``sdist`` or ``bdist_wheel`` will automatically downloaded the
package (cached in the .eggs directory).

Setup.py
++++++++

Your ``setup.py`` file should look like::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name=NAME,
      version='MAJ.MIN.PATCH',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension(NAME, [XX1, XX2])],
      setup_requires=['setuptools-zig'],
  )

with ``NAME`` replaced by the string that is your package name. MAJ, MIN, and PATCH
your package's version, and XX1, XX2 being your source files (you can have just
one, or more).

With that adapted to your project::

  python setup.py bdist_wheel

will result in a ``.whl`` file in your ``dist`` directory. That wheel file can be installed in a virtualenv,
and the functions defined in the package imported and used.


Using Zig as a C compiler
+++++++++++++++++++++++++

Create your ``setup.py``::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name='c_sum',
      version='1.0.0',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension('c_sum', ['sum.c', ])],
      setup_requires=['setuptools-zig'],

and ``sum.c``::

  /* based on https://docs.python.org/3.9/extending/extending.html */

  #define PY_SSIZE_T_CLEAN
  #include <Python.h>

  PyObject* sum(PyObject* self, PyObject* args) {
      long a, b;

      if (!PyArg_ParseTuple(args, "ll", &a, &b))
  	      return NULL;
      return PyLong_FromLong(a+b);
  }


  static struct PyMethodDef methods[] = {
      {"sum", (PyCFunction)sum, METH_VARARGS},
      {NULL, NULL}
  };

  static struct PyModuleDef zigmodule = {
      PyModuleDef_HEAD_INIT,
      "sum",
      NULL,
      -1,
      methods
  };

  PyMODINIT_FUNC PyInit_c_sum(void) {
      return PyModule_Create(&zigmodule);

install the resulting wheel using ``pip`` and use::

  python -c "from c_sum import sum; print(sum(20, 22))"

Using Zig with .zig and .c
++++++++++++++++++++++++++

The Zig compiler can easily mix and match here we use the C code to provide the
interface and do the heavy lifting of calculating the sum in Zig.

``setup.py``::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name='c_zig_sum',
      version='1.0.0',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension('c_zig_sum', ['c_int.c', 'sum.zig', ])],
      setup_requires=['setuptools-zig'],
  )

``c_int.c``::

  /* based on https://docs.python.org/3.9/extending/extending.html */

  #define PY_SSIZE_T_CLEAN
  #include <Python.h>

  PyObject* sum(PyObject* , PyObject*);

  /*
  PyObject* sum(PyObject* self, PyObject* args) {
      long a, b;

      if (!PyArg_ParseTuple(args, "ll", &a, &b))
          return NULL;
      return PyLong_FromLong(a+b);
  }
  */


  static struct PyMethodDef methods[] = {
      {"sum", (PyCFunction)sum, METH_VARARGS},
      {NULL, NULL}
  };

  static struct PyModuleDef zigmodule = {
      PyModuleDef_HEAD_INIT,
      "c_zig_sum",
      NULL,
      -1,
      methods
  };

  PyMODINIT_FUNC PyInit_c_zig_sum(void) {
      return PyModule_Create(&zigmodule);
  }

``sum.zig``::

  const c = @cImport({
      @cDefine("PY_SSIZE_T_CLEAN", "1");
      @cInclude("Python.h");
  });

  pub export fn sum(self: [*]c.PyObject, args: [*]c.PyObject) [*c]c.PyObject {
      var a: c_long = undefined;
      var b: c_long = undefined;
      if (!(c._PyArg_ParseTuple_SizeT(args, "ll", &a, &b) != 0)) return null;
      return c.PyLong_FromLong((a + b));


Zig code only
+++++++++++++

The orignal converted code is rather ugly to read. 
There were no differences in the program specific Zig code converted from C between 
Python 3.6/3.7/3.8/3.9 (but there was in the header).
This is a initial attempt to clean things up only the part under the comment line
should need adaption for your project.


``setup.py``::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name='zig_sum',
      version='1.0.1',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension('zig_sum', ['sum.zig' ])],
      setup_requires=['setuptools-zig'],
  )

``sum.zig``::

  // translated using zig from the C version

  const c = @cImport({
      @cDefine("PY_SSIZE_T_CLEAN", "1");
      @cInclude("Python.h");
  });

  const PyObject = c.PyObject;

  const PyMethodDef = extern struct {
      ml_name: [*c]const u8 = null,
      ml_meth: c.PyCFunction = null,
      ml_flags: c_int = 0,
      ml_doc: [*c]const u8 = null,
  };

  pub const PyModuleDef_Base = extern struct {
      ob_base: PyObject = c.PyObject,
      m_init: ?fn () callconv(.C) [*c]PyObject = null,
      m_index: c.Py_ssize_t = 0,
      m_copy: [*c]PyObject = null,
  };

  pub const PyModuleDef_HEAD_INIT = PyModuleDef_Base {
      .ob_base = PyObject {
          .ob_refcnt = 1,
          .ob_type = null,
      }
  };

  const PyModuleDef = extern struct {
      // m_base: c.PyModuleDef_Base,
      m_base: PyModuleDef_Base = PyModuleDef_HEAD_INIT,
      m_name: [*c]const u8,
      m_doc: [*c]const u8 = null,
      m_size: c.Py_ssize_t = -1,
      m_methods: [*]PyMethodDef,
      m_slots: [*c]c.struct_PyModuleDef_Slot = null,
      m_traverse: c.traverseproc = null,
      m_clear: c.inquiry = null,
      m_free: c.freefunc = null,
  };

  /////////////////////////////////////////////////

  pub export fn sum(self: [*]PyObject, args: [*]PyObject) [*c]PyObject {
      var a: c_long = undefined;
      var b: c_long = undefined;
      if (!(c._PyArg_ParseTuple_SizeT(args, "ll", &a, &b) != 0)) return null;
      return c.PyLong_FromLong((a + b));
  }

  pub var methods = [_:PyMethodDef{}]PyMethodDef{
      PyMethodDef{
          .ml_name = "sum",
          .ml_meth = @ptrCast(c.PyCFunction, @alignCast(@alignOf(fn ([*]PyObject, [*]PyObject) callconv(.C) [*]PyObject), sum)),
          .ml_flags = @as(c_int, 1),
          .ml_doc = null,
      },
  };

  pub var zigmodule = PyModuleDef{
      .m_name = "zig_sum",
      .m_methods = &methods,
  };

  pub export fn PyInit_zig_sum() [*c]c.PyObject {
      return c.PyModule_Create(@ptrCast([*c]c.struct_PyModuleDef, &zigmodule));



