Skip to content

Commit

Permalink
Reworked the neuron.hclass3 module (#1096)
Browse files Browse the repository at this point in the history
* added pyenv's .python-version to gitignore

* prevent import py2/3 module in py3/2. removed `exec` wrapper from hclass

* Changed comment and removed non-existing `nonlocal_hclass` import

* Removed traling whitespaces and double newline at eof

* Removed the MetaHocObject metaclass and added `nonlocal_hclass` factory

* New `HocBaseObject`; (nonlocal_)hclass uses this new base.

* Added dev note to demystify quintessential link between HOC & Python

* moved __all__ to PEP8 location

* Fixed error with grandchildren of `HocBaseObject`

* Apply changes only to Python 3.6+ where metaclass alternative is availbl

* fixed import typo

* hclass of hoc_type should precede use of hoc.HocObject API's

* Fixed usage of module_name

* added hclass35.py to Makefile, excessive trailing newlines.

* `module` does not exist, see details on mixed approaches in PR#1096

* black formatting & updated hclass2 derived docstrings

* Store dummy modules on `neuron` module to better emulate submodules

* Add a TypeError if init but not new has been given (see #1129)

* Added _pyobj_enabled flag to check whether Python object interface is up

* Explicitly  defined __new__ for 3.6+ pyobj compatibility

* Class composition is allowed if HOC types are the same

* added tests for HOC type Python classes, test only in Python 3.6+

* Removed 'super()' call for Python 2

* Revert "added hclass35.py to Makefile, excessive trailing newlines."

This reverts commit 080f14a.

* Added hclass35 to Makefile

* Revert "Removed traling whitespaces and double newline at eof"

This reverts commit 046fab4.

* Reinstated deprecation messages

* Merged hclass and nonlocal_hclass
  • Loading branch information
Helveg authored and alexsavulescu committed Apr 30, 2021
1 parent 6856136 commit 9046aee
Show file tree
Hide file tree
Showing 9 changed files with 476 additions and 90 deletions.
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
nmodlconf.h.in
nrnconf.h.in
build
.DS_Store
.eggs/
.idea/
CMakeFiles/
Makefile
Makefile.in
.deps
x86_64
__pycache__
venv
virtualenv
.python-version
*.o
*.lo

# These files are generated at build time
# It would be a good idea to create them in the
Expand Down
1 change: 1 addition & 0 deletions share/lib/python/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ neuron/units.py \
neuron/gui.py \
neuron/hclass2.py \
neuron/hclass3.py \
neuron/hclass35.py \
neuron/__init__.py \
neuron/psection.py \
neuron/sections.py \
Expand Down
77 changes: 45 additions & 32 deletions share/lib/python/neuron/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,24 @@ def test_rxd(exitOnError=True):
# using the idiom self.basemethod = self.baseattr('methodname')
# ------------------------------------------------------------------------------

import sys, types

# Flag for the Python objects interface
_pyobj_enabled = False
# Load the `hclass` factory for the correct Python version 2/3 and prevent the
# incorrect module source code from being opened by creating an empty module.
if sys.version_info[0] == 2:
hclass3 = sys.modules["neuron.hclass3"] = types.ModuleType("neuron.hclass3")
from neuron.hclass2 import hclass
else:
from neuron.hclass3 import hclass
hclass2 = sys.modules["neuron.hclass2"] = types.ModuleType("neuron.hclass2")
if sys.version_info[0] == 3 and sys.version_info[1] < 6:
import neuron.hclass35
hclass = neuron.hclass35.hclass
hclass3 = neuron.hclass35
else:
from neuron.hclass3 import HocBaseObject, hclass
_pyobj_enabled = True

# global list of paths already loaded by load_mechanisms
nrn_dll_loaded = []
Expand Down Expand Up @@ -370,13 +384,13 @@ def xopen(*args, **kwargs):
Description:
``h.xopen()`` executes the commands in ``hocfile``. This is a convenient way
to define user functions and procedures.
An optional second argument is the RCS revision number in the form of a
string. The RCS file with that revision number is checked out into a
temporary file and executed. The temporary file is then removed. A file
of the same primary name is unaffected.
``h.xopen()`` executes the commands in ``hocfile``. This is a convenient way
to define user functions and procedures.
An optional second argument is the RCS revision number in the form of a
string. The RCS file with that revision number is checked out into a
temporary file and executed. The temporary file is then removed. A file
of the same primary name is unaffected.
This function is deprecated and will be removed in a future release.
Use ``h.xopen`` instead.
"""
Expand All @@ -386,7 +400,7 @@ def xopen(*args, **kwargs):

def quit(*args, **kwargs):
"""
Exits the program. Can be used as the action of a button. If edit buffers
Exits the program. Can be used as the action of a button. If edit buffers
are open you will be asked if you wish to save them before the final exit.
This function is deprecated and will be removed in a future release.
Expand All @@ -395,7 +409,7 @@ def quit(*args, **kwargs):
"""
warnings.warn("neuron.quit() is deprecated; use h.quit() or sys.exit() instead", DeprecationWarning, stacklevel=2)
return h.quit(*args, **kwargs)



def hoc_execute(hoc_commands, comment=None):
Expand Down Expand Up @@ -447,55 +461,55 @@ def init():
By default, the units used by h.finitialize are in mV, but you can be explicit using
NEURON's unit's library, e.g.
.. code-block:: python
from neuron.units import mV
h.finitialize(-65 * mV)
https://www.neuron.yale.edu/neuron/static/py_doc/simctrl/programmatic.html?#finitialize
"""
warnings.warn("neuron.init() is deprecated; use h.init() instead", DeprecationWarning, stacklevel=2)

h.finitialize()

def run(tstop):
"""
function run(tstop)
Run the simulation (advance the solver) until tstop [ms]
`h.run()` and `h.continuerun(tstop)` are more powerful solutions defined in the `stdrun.hoc` library.
** This function exists for historical purposes. Use in new code is not recommended. **
This function is deprecated and will be removed in a future
release.
release.
For running a simulation, consider doing the following instead:
Begin your code with
.. code-block:: python
from neuron import h
from neuron.units import ms, mV
h.load_file('stdrun.hoc')
Then when it is time to initialize and run the simulation:
.. code-block:: python
h.finitialize(-65 * mV)
h.continuerun(100 * ms)
where the initial membrane potential and the simulation run time are adjusted as appropriate
for your model.
"""
warnings.warn("neuron.run(tstop) is deprecated; use h.stdinit() and h.continuerun(tstop) instead", DeprecationWarning, stacklevel=2)

h('tstop = %g' % tstop)
h('while (t < tstop) { fadvance() }')
# what about pc.psolve(tstop)?
Expand Down Expand Up @@ -667,7 +681,7 @@ def _declare_contour(secobj, obj, name):
center_vec = secobj.contourcenter(secobj.raw.getrow(0), secobj.raw.getrow(1), secobj.raw.getrow(2))
x0, y0, z0 = [center_vec.x[i] for i in range(3)]
# store a couple of points to check if the section has been moved
pts = [(sec.x3d(i),sec.y3d(i),sec.z3d(i)) for i in [0, sec.n3d()-1]]
pts = [(sec.x3d(i),sec.y3d(i),sec.z3d(i)) for i in [0, sec.n3d()-1]]
# (is_stack, x, y, z, xcenter, ycenter, zcenter)
_sec_db[sec.hoc_internal_name()] = (True if secobj.contour_list else False, secobj.raw.getrow(0).c(j), secobj.raw.getrow(1).c(j), secobj.raw.getrow(2).c(j), x0, y0, z0, pts)

Expand Down Expand Up @@ -796,7 +810,7 @@ class _RangeVarPlot(_WrapperPlot):
fig.show()
pyplot.show()
# plotnine/ggplot
p9.ggplot() + r.plot(p9)
Expand Down Expand Up @@ -1060,10 +1074,10 @@ def mark(self, segment, marker='or', **kwargs):
if secs is None:
secs = list(h.allsec())


if variable is None:
kwargs.setdefault('color', 'black')

data = []
for sec in secs:
xs = [sec.x3d(i) for i in range(sec.n3d())]
Expand Down Expand Up @@ -1392,7 +1406,7 @@ def _nrnpy_rvp_pyobj_callback(f):
f_type = str(type(f))
if f_type not in ("<class 'neuron.rxd.species.SpeciesOnRegion'>", "<class 'neuron.rxd.species.Species'>"):
return f

# if we're here, f is an rxd variable, and we return a function that looks
# up the weighted average concentration given an x and h.cas()
# this is not particularly efficient so it is probably better to use this for
Expand Down Expand Up @@ -1432,4 +1446,3 @@ def clear_gui_callback():
nrnpy_set_gui_callback(None)
except:
pass

62 changes: 37 additions & 25 deletions share/lib/python/neuron/hclass2.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,53 @@
#Python2 only
# Python2 only
# ------------------------------------------------------------------------------
# class factory for subclassing h.anyclass
# h.anyclass methods may be overridden. If so the base method can be called
# using the idiom self.basemethod = self.baseattr('methodname')
# ------------------------------------------------------------------------------

from neuron import h, hoc
import nrn

#avoid syntax error if compiled by python 3
exec('''

class MetaHocObject(type):
"""Provides Exception for Inheritance of multiple HocObject"""
def __new__(cls, name, bases, attrs):
#print cls, name, bases
m = []
for b in bases:
if issubclass(b, hoc.HocObject):
m.append(b.__name__)
if (len(m) > 1):
raise TypeError('Multiple Inheritance of HocObject in %s' % name
+ ' through %s not allowed' % ','.join(m))
#note that join(b.__name__ for b in m) is not valid for Python 2.3
return type.__new__(cls, name, bases, attrs)
"""
Provides error when trying to compose a class of multiple HOC types.
"""

def __new__(cls, name, bases, attrs):
m = []
for b in bases:
if issubclass(b, hoc.HocObject):
m.append(b.__name__)
if len(m) > 1:
raise TypeError(
"Multiple Inheritance of HocObject in %s" % name
+ " through %s not allowed" % ",".join(m)
)

return type.__new__(cls, name, bases, attrs)


def hclass(c):
"""Class factory for subclassing h.anyclass. E.g. class MyList(hclass(h.List)):..."""
if c == h.Section :
"""
Class factory for subclassing any HOC type.
Example:
.. code-block:: python
class MyList(hclass(h.List)):
pass
"""
if c == h.Section:
return nrn.Section
#class hc(hoc.HocObject, metaclass=MetaHocObject):

class hc(hoc.HocObject):
def __new__(cls, *args, **kwds):
kwds2 = {'hocbase': cls.htype}
if 'sec' in kwds:
kwds2['sec'] = kwds['sec']
kwds2 = {"hocbase": cls.htype}
if "sec" in kwds:
kwds2["sec"] = kwds["sec"]
return hoc.HocObject.__new__(cls, *args, **kwds2)
setattr(hc, 'htype', c)

setattr(hc, "htype", c)
return hc
''')
Loading

0 comments on commit 9046aee

Please sign in to comment.