pax_global_header00006660000000000000000000000064130063373630014516gustar00rootroot0000000000000052 comment=d1d0776cf198ee8a1fa462875ab3d5e4d92f9d22 traits-4.6.0/000077500000000000000000000000001300633736300130335ustar00rootroot00000000000000traits-4.6.0/.coveragerc000066400000000000000000000006701300633736300151570ustar00rootroot00000000000000[run] branch = True source = traits omit = */tests/* [report] # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain about missing debug-only code: def __repr__ # Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError if __name__ == .__main__.: ignore_errors = True traits-4.6.0/.gitignore000066400000000000000000000002641300633736300150250ustar00rootroot00000000000000# file types to ignore *.pyc *.pyd *.so *.enamlc *~ .DS_Store # ignore the build directories *.egg-info/ build/ dist/ docs/build/ # Auto-generated by setup.py traits/_version.py traits-4.6.0/.travis.yml000066400000000000000000000011011300633736300151350ustar00rootroot00000000000000language: python sudo: false python: - 2.6 - 2.7 - 3.3 - 3.4 - 3.5 branches: only: master cache: directories: - $HOME/.cache - $HOME/.ccache before_install: - ccache -s - export PATH=/usr/lib/ccache:${PATH} - pip install --upgrade pip install: - pip install --upgrade -r travis-ci-requirements.txt - python setup.py develop before_script: - mkdir testrunner - cp .coveragerc testrunner/ - cd testrunner script: - coverage run -m nose.core traits --exe notifications: email: - travis-ci@enthought.com after_success: coveralls traits-4.6.0/CHANGES.txt000066400000000000000000000162621300633736300146530ustar00rootroot00000000000000Traits CHANGELOG ================ Release 4.6.0 ------------- This is an incremental release over 4.5, accumulating over a year's worth of bugfixes and small improvements to the code. Highlights of this release include: * support for Python 3.4 and 3.5. * new Bytes and ValidatedTuple traits. * a new ArrayOrNone trait which correctly handles None comparisons with Numpy arrays. * clean-up of the ETSConfig code for TraitsUI toolkit selection. * better compatibility with NumPy scalar types. * many other bugfixes and improvements. Change summary since 4.5.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~ Enhancements * Added a ``Bytes`` Trait and related traits (#329) * Added support for finding resources from zipped Python source code (#316) * Added in-place set arithmetic operations for ``TraitSetObject``s and accept match behaviour of ``TraitSetObject`` with regular Python sets when performing operations with non-set types (eg. lists, dictionaries) (#289) * Added a context manager to allow provisional selection of a toolkit to ``ETSConfig`` (this generally improves reliability of toolkit selection for Pyface and TraitsUI). (#276) * Added Trait change recorder to aid in debugging event-driven code. (#139) * ``__iadd__`` and ``__imul__`` implemented on TraitListObjects. (#165) * Added new ``ArrayOrNone`` trait type to replace the ``Either(None, Array)`` idiom. The old idiom results in warnings on NumPy >= 1.9. (#219) * Added a new ``ValidatedTuple`` trait that supports custom validation. (#205) Changes * Removed redundant, internal ``ETSConfig`` from Traits codebase. (#327) * Better error reporting for failed attribute access. (#243) * Removed buggy ``-toolkit`` commandline option ``ETSConfig``. (#326) * Removed buggy ``*names`` positional arguments from ``on_trait_change`` decorator in improved argument passing (#207). * Allow ``Float`` and ``BaseFloat`` traits to accept Python longs. (#272) * Clean-up and fixes to example code. (#126) * Remove outdated ``ImportSpy`` and ``ImportManager`` utilities. (#188) * The ``deprecated`` decorator now issues a DeprecationWarning (using the Python ``warnings`` module) rather than logging a warning via the ``logging`` machinery. It no longer tries to remember when a warning has been previously issued. (#220) * Deprecated ``HasTraits.get()`` and ``HasTraits.set()`` (#190). * The default ``View`` shows all (non-event) traits whose ``visible`` property is not ``False``. Private traits are set ``visible=False`` by default. (#234) Fixes * Fix Bool traits so that value stored is always a Python ``bool`` (and in particular, not a NumPy ``np.bool_``). (#318) * Fix Bool traits so that regular validator accepts NumpPy's ``np.bool_`` boolean values (bringing it in agreement with the fast validator). (#302) * Fix use of ``next`` in ``TraitDocumenter`` for Python 3 compatibility. (#293) * Fix off-by-one error when ``TraitListObject`` is setting or deleting slices. (#283) * Fix reference cycles caused by ``sync_traits``. (#135) * Fix so that ``sys.exc_info()`` works as expected in exception handlers in Python 3 (#266) * Fix ``String`` trait to accept ``str`` subclasses (like ``numpy.str_``). (#267) * Fixed incorrect in list events for ``insert`` operations with an index outside the range [``-len(target_list)``, ``len(target_list)``]. (#165) * Fix incorrect behaviour of ``check_implements`` for overridden methods. (#192) * Fix error when trying to listen to traits using list bracket notation. (#195) * Fix reference leak in ``CHasTraits._notifiers``. (#248) * Fix reference leak from use of ``DelegatesTo``. (#260) * Instance traits weren't included in the result of ``traits()``. (#234) Release 4.5.0 ------------- Traits is now compatible with Python 3! The library now supports Python 3.2 and 3.3. The release also includes increased code coverage and automatic coverage report through coveralls.io. Change summary since 4.4.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~ Enhancements * Test files cleanups (#108, #111, #121) * Add automatic coverage reports (#110, #122) * Removed obsolete code (#109, #112, #113) * Increased test coverage (#114, #118) * Python 3 support (#115). Thanks Yves Delley. * Allow setting and resetting the global adaptation manager (#145) * Various documentation improvements (#132, #133, #148, #154). Changes * The Int trait type now accepts Python ints *and* Python longs, as well as instances of any Python type that implements the ``__index__`` method. Previously, long instances were not accepted. (#104, #123). Fixes * Fix crash when trying to validate a property that has been deleted. (#138) * Fix clearing exception when raising a TraitError (#119) * Fix automatic adaptation when assigning to List trait (#147) * Fix some ctraits refcounting and exception clearing bugs (#48). Thanks Yves Delley. Release 4.4.0 ------------- The major new feature in this release is a new adaptation mechanism in the ``traits.adaptation`` package. The new mechanism is intended to replace the older traits.protocols package. Code written against ``traits.protocols`` will continue to work, although the ``traits.protocols`` API has been deprecated, and a warning will be logged on first use of ``traits.protocols``. See the 'Advanced Topics' section of the user manual for more details. The release also includes improved support for using Cython with ``HasTraits`` classes, some new helper utilities for writing unit tests for Traits events, and a variety of bug fixes, stability enhancements, and internal code improvements. Change summary since 4.3.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~ New features * The adaptation mechanism in Traits, formerly based on the 'traits.protocols' package, has been replaced with the more robust 'traits.adaptation' package. (#51) * Added utility function for importing symbols (name, classes, functions) by name: 'traits.util.api.import_symbol'. (#51) * Users can set a global tracer, which receives all traits change events: ``traits.trait_notifiers.set_change_event_tracers``. (#79) Enhancements * Update benchmark script. (#54) * traits.util.deprecated: use module logger instead of root logger. (#59) * Provide an informative message in AdaptationError. (#62) * Allow HasTraits classes to be cythonized. (#73) * Improve tests for cythonization support. (#75) * Extending various trait testing helpers (#53) Refactoring * The Traits notification code has been reworked to remove code duplication, and test coverage of that code has been significantly improved. (#79) Fixes * Fix race condition when removing a traits listener. (#57) * Fix ugly interaction between DelegatesTo change handlers, dynamic change handlers and two levels of dynamic intialization. (#63) * Use a NullHandler for all 'traits' loggers. (#64) * Fix race condition in TraitChangeNotifyWrapper.listener_deleted (#66) * Fix leaking notifiers. (#68) * Fix failing special instance trait events. (#78) * Fix hiding KeyError exception inside trait default initialize method. (#81) * Fix Adapter object initialization. (#93) * Fix cyclic garbage arising from use of the WeakRef trait type. (#95) * ``TraitSetObject.copy`` now returns a plain rather than an uninitialized ``TraitSetObject`` instance. (#97) * Fix cyclic garbage arising from dynamic trait change handlers. (#101) traits-4.6.0/LICENSE.txt000066400000000000000000000031251300633736300146570ustar00rootroot00000000000000This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. Copyright (c) 2006-2016, Enthought, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Enthought, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. traits-4.6.0/MANIFEST.in000066400000000000000000000006461300633736300145770ustar00rootroot00000000000000include CHANGES.txt include LICENSE.txt include MANIFEST.in include README.rst include image_LICENSE.txt include image_LICENSE_Eclipse.txt include image_LICENSE_Nuvola.txt graft docs prune docs/build recursive-exclude docs *.pyc graft examples recursive-exclude examples *.pyc include traits/ctraits.c include traits/py2to3.h include traits/protocols/README.txt recursive-include fixers *.py recursive-include traits *.py traits-4.6.0/README.rst000066400000000000000000000050351300633736300145250ustar00rootroot00000000000000============================================== traits: explicitly typed attributes for Python ============================================== http://docs.enthought.com/traits .. image:: https://api.travis-ci.org/enthought/traits.png?branch=master :target: https://travis-ci.org/enthought/traits :alt: Build status .. image:: https://coveralls.io/repos/enthought/traits/badge.png :target: https://coveralls.io/r/enthought/traits :alt: Coverage status The Traits project is at the center of all Enthought Tool Suite development and has changed the mental model used at Enthought for programming in the already extremely efficient Python programming language. We encourage everyone to join us in enjoying the productivity gains from using such a powerful approach. The Traits project allows Python programmers to use a special kind of type definition called a *trait*, which gives object attributes some additional characteristics: - **Initialization**: A trait has a *default value*, which is automatically set as the initial value of an attribute before its first use in a program. - **Validation**: The type of a trait attribute is *explicitly declared*. The type is evident in the code, and only values that meet a programmer-specified set of criteria (i.e., the trait definition) can be assigned to that attribute. - **Delegation**: The value of a trait attribute can be contained either in the defining object or in another object *delegated* to by the trait. - **Notification**: Setting the value of a trait attribute can *notify* other parts of the program that the value has changed. - **Visualization**: User interfaces that allow a user to *interactively modify* the value of a trait attribute can be automatically constructed using the trait's definition. (This feature requires that a supported GUI toolkit be installed. If this feature is not used, the Traits project does not otherwise require GUI support.) A class can freely mix trait-based attributes with normal Python attributes, or can opt to allow the use of only a fixed or open set of trait attributes within the class. Trait attributes defined by a class are automatically inherited by any subclass derived from the class. Dependencies ------------ Traits has the following optional dependencies: * `NumPy `_ to support the trait types for arrays. * `Traitsui `_ to support Gui Views. To build the full documentation one needs: * sphinx > 1.2.3 * mock (optional if traitsui is not available) traits-4.6.0/appveyor-install.cmd000066400000000000000000000007141300633736300170330ustar00rootroot00000000000000"%sdkverpath%" -q -version:"%sdkver%" call setenv /x64 rem install python packages pip install --cache-dir C:/egg_cache nose pip install --cache-dir C:/egg_cache coverage==3.7.1 pip install --cache-dir C:/egg_cache numpy pip install --cache-dir C:/egg_cache cython rem Work around bug in babel 2.0: see mitsuhiko/babel#174 pip install --cache-dir C:/egg_cache babel==1.3 pip install --cache-dir C:/egg_cache Sphinx rem install traits python setup.py develop traits-4.6.0/appveyor-test.cmd000066400000000000000000000003171300633736300163430ustar00rootroot00000000000000"%sdkverpath%" -q -version:"%sdkver%" call setenv /x64 mkdir testrun copy .coveragerc testrun cd testrun coverage run -m nose.core -v traits --exe if %errorlevel% neq 0 exit /b %errorlevel% coverage report traits-4.6.0/appveyor.yml000066400000000000000000000015701300633736300154260ustar00rootroot00000000000000build: false shallow_clone: true environment: global: distutils_use_sdk: 1 matrix: - python: "C:/Python27-x64" sdkver: "v7.0" - python: "C:/Python33-x64" sdkver: "v7.1" - python: "C:/Python34-x64" sdkver: "v7.1" cache: - c:\egg_cache init: - ps: $Env:sdkbin = "C:\Program Files\Microsoft SDKs\Windows\" + $Env:sdkver + "\Bin" - ps: $Env:sdkverpath = "C:/Program Files/Microsoft SDKs/Windows/" + $Env:sdkver + "/Setup/WindowsSdkVer.exe" - ps: $Env:path = $Env:python + ";" + $Env:python + "\scripts;" + $Env:sdkbin + ";" + $Env:path install: - ps: if ((Test-Path "c:/egg_cache") -eq 0) { mkdir c:/egg_cache } - ps: python -m pip install --upgrade --no-binary wheel pip - ps: pip install --upgrade wheel - ps: pip --version - cmd /v:on /e:on /c ".\appveyor-install.cmd" test_script: - cmd /v:on /e:on /c ".\appveyor-test.cmd" traits-4.6.0/docs/000077500000000000000000000000001300633736300137635ustar00rootroot00000000000000traits-4.6.0/docs/CHANGES.txt000066400000000000000000000065171300633736300156050ustar00rootroot00000000000000Traits 3.5.1 (not yet released) =============================== Traits 3.5.0 (Oct 15, 2010) =========================== Enhancements ------------ * adding support for drop-down menu in Button traits, but only for qt backend * adding 'show_notebook_menu' option to ListEditor so that the user can right-click and show or hide the context menu (Qt) * added selection range traits to make it possible for users to replace selected text Fixes ----- * fixed null color editor to work with tuples * bug when opening a view with the ToolbarButton Traits 3.4.0 (May 26, 2010) =========================== Enhancements ------------ * adding new example to make testing rgb color editor easier Fixes ----- * fixed NumericColumn to not expect object to have model_selection attribute, and removed more dead theming code * fixed API bugs with the NumericColumn where its function signatures differed from its base class, but the calling code expected them to all be the same * fixed bug which was related to type name errors caused when running Sphinx * when using File(exists=True), be sure to validate the type of the value first before using os.path.isfile() Traits 3.3.0 (Feb 24, 2010) =========================== Enhancements ------------ The major enhancement this release is that the entire Traits package has been changed to use relative imports so that it can be installed as a sub-package inside another larger library or package. This was not previously possible, since the various modules inside Traits would import each other directly through "traits.[module]". Many thanks to Darren Dale for the patch. Fixes ----- There have been numerous minor bugfixes since the last release. The most notable ones are: * Many fixes involve making Traits UI more robust if wxPython is not installed on a system. In the past, we have been able to use Qt if it was also installed, but removing Wx would lead to a variety of little bugs in various places. We've squashed a number of these. We've also added better checks to make sure we're selecting the right toolkit at import and at runtime. * A nasty cyclic reference was discovered and eliminated in DelegatesTo traits. * The Undefined and Uninitialized Traits were made into true singletons. * Much of the inconsistent formatting across the entire Traits source has been eliminated and normalized (tabs/spaces, line endings). Traits 3.2.0 (July 15, 2009) ============================ Enhancements ------------ * Implemented editable_labels attribute in the TabularEditor for enabling editing of the labels (i.e. the first column) * Saving/restoring window positions works with multiple displays of different sizes * New ProgressEditor * Changed default colors for TableEditor * Added support for HTMLEditor for QT backend using QtWebKit * Improved support for opening links in external browser from HTMLEditor * Added support for TabularEditor for QT backend * Added support for marking up the CodeEditor, including adding squiggles and dimming lines * Added SearchEditor * Improved unicode support * Changed behavior of RangeEditor text box to not auto-set * Added support in RangeEditor for specifying the method to evaluate new values. * Add DefaultOverride editor factory courtesy Stéfan van der Walt * Removed sys.exit() call from SaveHandler.exit() Fixes ----- traits-4.6.0/docs/Makefile000066400000000000000000000072531300633736300154320ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/traits.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/traits.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." traits-4.6.0/docs/make.bat000066400000000000000000000145101300633736300153710ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SimPhoNy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SimPhoNy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end traits-4.6.0/docs/source/000077500000000000000000000000001300633736300152635ustar00rootroot00000000000000traits-4.6.0/docs/source/.static/000077500000000000000000000000001300633736300166305ustar00rootroot00000000000000traits-4.6.0/docs/source/.static/default.css000066400000000000000000000323121300633736300207670ustar00rootroot00000000000000/** * Sphinx Doc Design */ body { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; font-size: 100%; background-color: #333333; color: #000; margin: 0; padding: 0; } /* :::: LAYOUT :::: */ div.document { background-color: #24326e; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: white; padding: 0 20px 30px 20px; } div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } p.logo { text-align: center; } div.clearer { clear: both; } div.footer { color: #fff; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: #fff; text-decoration: underline; } div.related { background-color: #24326e; color: #fff; width: 100%; height: 30px; line-height: 30px; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } div.related a { color: white; } /* ::: TOC :::: */ div.sphinxsidebar h3 { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; color: #acafb3; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h4 { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; color: #acafb3; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: white; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; list-style: none; color: white; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar a { color: #fff; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #9bbde2; font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; font-size: 1em; } /* :::: MODULE CLOUD :::: */ div.modulecloud { margin: -5px 10px 5px 10px; padding: 10px; line-height: 160%; border: 1px solid #666666; background-color: #dddddd; } div.modulecloud a { padding: 0 5px 0 5px; } /* :::: SEARCH :::: */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #666; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* :::: COMMON FORM STYLES :::: */ div.actions { padding: 5px 10px 5px 10px; border-top: 1px solid #598ec0; border-bottom: 1px solid #598ec0; background-color: #9bbde2; } form dl { color: #333; } form dt { clear: both; float: left; min-width: 110px; margin-right: 10px; padding-top: 2px; } input#homepage { display: none; } div.error { margin: 5px 20px 0 0; padding: 5px; border: 1px solid #db7d46; font-weight: bold; } /* :::: INLINE COMMENTS :::: */ div.inlinecomments { position: absolute; right: 20px; } div.inlinecomments a.bubble { display: block; float: right; background-image: url(style/comment.png); background-repeat: no-repeat; width: 25px; height: 25px; text-align: center; padding-top: 3px; font-size: 0.9em; line-height: 14px; font-weight: bold; color: black; } div.inlinecomments a.bubble span { display: none; } div.inlinecomments a.emptybubble { background-image: url(style/nocomment.png); } div.inlinecomments a.bubble:hover { background-image: url(style/hovercomment.png); text-decoration: none; color: #598ec0; } div.inlinecomments div.comments { float: right; margin: 25px 5px 0 0; max-width: 50em; min-width: 30em; border: 1px solid #598ec0; background-color: #9bbde2; z-index: 150; } div#comments { border: 1px solid #598ec0; margin-top: 20px; } div#comments div.nocomments { padding: 10px; font-weight: bold; } div.inlinecomments div.comments h3, div#comments h3 { margin: 0; padding: 0; background-color: #598ec0; color: white; border: none; padding: 3px; } div.inlinecomments div.comments div.actions { padding: 4px; margin: 0; border-top: none; } div#comments div.comment { margin: 10px; border: 1px solid #598ec0; } div.inlinecomments div.comment h4, div.commentwindow div.comment h4, div#comments div.comment h4 { margin: 10px 0 0 0; background-color: #2eabb0; color: white; border: none; padding: 1px 4px 1px 4px; } div#comments div.comment h4 { margin: 0; } div#comments div.comment h4 a { color: #9bbde2; } div.inlinecomments div.comment div.text, div.commentwindow div.comment div.text, div#comments div.comment div.text { margin: -5px 0 -5px 0; padding: 0 10px 0 10px; } div.inlinecomments div.comment div.meta, div.commentwindow div.comment div.meta, div#comments div.comment div.meta { text-align: right; padding: 2px 10px 2px 0; font-size: 95%; color: #598ec0; border-top: 1px solid #598ec0; background-color: #9bbde2; } div.commentwindow { position: absolute; width: 500px; border: 1px solid #598ec0; background-color: #9bbde2; display: none; z-index: 130; } div.commentwindow h3 { margin: 0; background-color: #598ec0; color: white; border: none; padding: 5px; font-size: 1.5em; cursor: pointer; } div.commentwindow div.actions { margin: 10px -10px 0 -10px; padding: 4px 10px 4px 10px; color: #598ec0; } div.commentwindow div.actions input { border: 1px solid #598ec0; background-color: white; color: #073d61; cursor: pointer; } div.commentwindow div.form { padding: 0 10px 0 10px; } div.commentwindow div.form input, div.commentwindow div.form textarea { border: 1px solid #598ec0; background-color: white; color: black; } div.commentwindow div.error { margin: 10px 5px 10px 5px; background-color: #fff2b0; display: none; } div.commentwindow div.form textarea { width: 99%; } div.commentwindow div.preview { margin: 10px 0 10px 0; background-color: ##9bbde2; padding: 0 1px 1px 25px; } div.commentwindow div.preview h4 { margin: 0 0 -5px -20px; padding: 4px 0 0 4px; color: white; font-size: 1.3em; } div.commentwindow div.preview div.comment { background-color: #f2fbfd; } div.commentwindow div.preview div.comment h4 { margin: 10px 0 0 0!important; padding: 1px 4px 1px 4px!important; font-size: 1.2em; } /* :::: SUGGEST CHANGES :::: */ div#suggest-changes-box input, div#suggest-changes-box textarea { border: 1px solid #666; background-color: white; color: black; } div#suggest-changes-box textarea { width: 99%; height: 400px; } /* :::: PREVIEW :::: */ div.preview { background-image: url(style/preview.png); padding: 0 20px 20px 20px; margin-bottom: 30px; } /* :::: INDEX PAGE :::: */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* :::: INDEX STYLES :::: */ table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #dddddd; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } form.pfform { margin: 10px 0 20px 0; } /* :::: GLOBAL STYLES :::: */ .docwarning { background-color: #fff2b0; padding: 10px; margin: 0 -20px 0 -20px; border-bottom: 1px solid #db7d46; } p.subhead { font-weight: bold; margin-top: 20px; } a { color: #24326e; text-decoration: none; } a:hover { text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; background-color: #dddddd; font-weight: normal; color: #073d61; border-bottom: 1px solid #666; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #edaa1e; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } a.headerlink:hover { background-color: #edaa1e; color: white; } div.body p, div.body dd, div.body li { text-align: left; line-height: 130%; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } ul.fakelist { list-style: none; margin: 10px 0 10px 20px; padding: 0; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } /* "Footnotes" heading */ p.rubric { margin-top: 30px; font-weight: bold; } /* "Topics" */ div.topic { background-color: #ddd; border: 1px solid #666; padding: 0 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* Admonitions */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } div.admonition p { display: inline; } div.seealso { background-color: #fff2b0; border: 1px solid #edaa1e; } div.warning { background-color: #fff2b0; border: 1px solid ##db7d46; } div.note { background-color: #eee; border: 1px solid #666; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; display: inline; } p.admonition-title:after { content: ":"; } div.body p.centered { text-align: center; margin-top: 25px; } table.docutils { border: 0; } table.docutils td, table.docutils th { padding: 1px 8px 1px 0; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #a9a6a2; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } dl { margin-bottom: 15px; clear: both; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } .refcount { color: #24326e; } dt:target, .highlight { background-color: #edaa1e1; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } th { text-align: left; padding-right: 5px; } pre { padding: 5px; background-color: #e6f3ff; color: #333; border: 1px solid #24326e; border-left: none; border-right: none; overflow: auto; } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt { background-color: #ddd; padding: 0 1px 0 1px; font-size: 1.2em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } .footnote:target { background-color: #fff2b0 } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } form.comment { margin: 0; padding: 10px 30px 10px 30px; background-color: #ddd; } form.comment h3 { background-color: #598ec0; color: white; margin: -10px -30px 10px -30px; padding: 5px; font-size: 1.4em; } form.comment input, form.comment textarea { border: 1px solid #ddd; padding: 2px; font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; font-size: 100%; } form.comment input[type="text"] { width: 240px; } form.comment textarea { width: 100%; height: 200px; margin-bottom: 10px; } .system-message { background-color: #edaa1e; padding: 5px; border: 3px solid red; } /* :::: PRINT :::: */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0; width : 100%; } div.sphinxsidebar, div.related, div.footer, div#comments div.new-comment-box, #top-link { display: none; } } traits-4.6.0/docs/source/.static/e-logo-rev.png000066400000000000000000000075111300633736300213160ustar00rootroot00000000000000PNG  IHDRoi*sRGB pHYs  tIMELIDATx]ktT>z\$m ȥ!TD jV|_CWph PT\"B$$6aIf9qfB%= By}; vo`!4ChVv[12 !HGiyg):QVd8SmsQAj2~~AH3qU:?!4[a6SRu ǎ7H'1"_Qq!JbBwx$I-S^QO>AO~( HAPU)=Ojjm+ v$~ئ"33zviJn[*.\v(/E1U`Ycֿ&y3g>=x$;GS@]d1YÓo"۾X6n8o2 ,c_܊U?y" "cdL5HfFj~}Q]H錩/Oxcq'~lӕ_ ţeW\| &cLMhdȶ9-՗ $ Θڳ9i˗>xa6>#E _h2$}앿"a\l߰0/"ޑҦ.*:UQyٕ~`:oYfxu? b)<̜>җ'rYgԾ6ngeSMkm>uv" Snhj ̌ry_ݚLM01@$(]vƏ{_{#&>4l|c.8~rK05bjԈm;14*:Ο3yK|ީT\> 8nd٤B]j맻]8#&[5TEUlu#u\/kk^6t=Zo`Ӌ-,R'*EP1#EQ DfsnlOYYYҨ!${G2yZ~\pN|olӋnϯBu-\$5˘TYgNR^\8gF{@|4Ņ0ov2֊^:j)D"zM En1]WfN@wǛ뿨k B|c!>8T'JԉaZxubOW~;c%dLynظedNSt~WX\f-pO',9UI21`xĥd  ,{ER"Z G 4PLq@$#15! G}\.-2kEfV=G15Q&ph!9Ce Cvj(# 5#GX:InHJZmڞU__(h݆' H7cHκ})"Db-&`i\eU?*YJ05 D S[GabDěrqEʪ9կm"4LwtGTدr{OPۿhj?:}"i b:/7yA@eK#$t13mj51K &^w !%PSSSֆlr{s^#w4DmQI S#3a@57Q; S#:į v4yR+A&P0j/))-&Z4S.[Z2d^!j8J01-j(T!05Q)"jԌ+@vpd"'4LuyC͉cv,@A1i_qLq|s4bvGz!U !KIQD1E3[1vI $00h6FL̙dnu˞?SScw\LGaʃcf-N]y/4u: c c PM18_h>4~h޽f l%&N^>?2=iC)9v!˜j>hN'N~(aİ}Wx+' u0?1sL _/>_nH ! x9zq@bzlLؘO_6Ac6~t=F&מc2\汋rh3.婓Jx`x^_>_mqKkj+-++Y.zw3TU+qܹ~M\_:pBI" D5 JcTubd!P%+~fz*EP]6R2;/uz] g,'Nd=C^n188D,dZ}W/)~ǎ/z~*0P]g*ݐ[{s]b76 $?`[퍘JTDDKŽ t "((}qqwZΦO11fZ XSXk71E~;{GbN#"k" r@4˗mrN"srLڀ?Vh?݁nw'?0l۶`bF4]2UU ;llgL bkx'ۄ&%QU#c*B{awE|DǶBhZ-f/wIENDB`traits-4.6.0/docs/source/.static/et.ico000066400000000000000000000236261300633736300177450ustar00rootroot00000000000000(f 00hvh F00( r5(H73iKD]ZZzols$jV4{H5Fg@Hg @' 9c]sE{c@Ec{bFQEPkG\ 1}m( @_-!p3%7)&[B<]VU~okWU#XW$iU$zu5{j6Yv1GWw"X f$I qJ QJ[JډJc4jJ5{J 1FJ qGI Q#W6 Q$j#XA${#h1IQ$j1< s4{A a5S Q}A\1J7|xp``?`8(0`W,"4(%p3%v>1IGF~QFja_~wtdFwG܆ h즋" ij""!zi"""!g""""ۆ"""""F얋""""""Gzޥ""""""Hi """"" ih s"""""  c"""""! R""""!|2"""!|{2""!|2"1"!5 y@y`"!""" 2""""G s"""""H b"""" i R""""!yR""""!qh2""""0i2""""GR"!z"""""hr"""r"""" """""FB""""#93"""""GB"""""62"""""hp"""""%""""" ht"""""#|"""""!"""""k """""""""""Y s"""""""""8 b"""""""&R"""""%2"""#|2"#k"Y?????( f-t1!j/#i1$n3$j2&r4&s4&s5&u5&]0'`1'^2'u5'v5'v6'x6';+(X2(_2(r4(u6(w7(x7(z7(y8(z8({8(P0)s7)z8){8)|9):*c5,i9-A1.R4.E3/W7/u=1x?2A97>;:{F:F?>{I>mH?DBAMDB~ODJHHOMLhSOQPPPQQSRRUTTaW\YXx_[c[]]]^^^b_^___dddpghhhmhkjijjjrksmnnnrpopppsssuttwwwyyy~{}}}t@4LhO\b$8PraKx_ *?VNdcpI 0M}Wfm:1n{|wFvu2!5yziAUSyxj?]GRryxj>E-BYyzk7l`, %3Jg~Z+DaT)9hzC&6Ooe" [q. *>;'#QX( <H/s=^( @m,6#>%D&K'O(R)P) Y+ q0!2%"`."8'#g1#4($l2%p3%q3%q4%0'&a0&r4&t5&u5&v5&e2'p4't4't5'u5'u6'v6'w6'x6'/)(r6(v6(x7(Y3)z8)v8*l6+2--m:-v;-w>1543Q93v@3lA7vC8;:9wF:><;N?<VA=?>>@??zK@AAAoJAFFF~SHbNIKKKnQKLLLrTMONNPNNZPQQQSRRVVUdXVXWWaWaX[ZZwa\d_^e^i_```iabbbedceedhhhphqh}mipijjjlkkplrlwmoooqpopppxqwrsssyvuwwwyyy{{}}}~~~ŻjjybO =iٙWa"HuihZ!.SWZ% 9bݫXxaBmnczq2 !*NwܐVr-% 3]wt\#%XzF' Dz:  EIv- N}I`')uHYH޾Hn65S|HJ' 9dH<%"DpHs0!.SDe(! ;g9pQ("GuM8b> .Uo0@m~1% KL  *Nwג7%%?4%% 3XжP& +_l,$ =g^/#AT($ ! 1>  )[4 #Ck,1{R(%+_f|xp``?`8(0`<5"=$H&/"S)m-*" -# X+ s0!b."o1"+%#h0#+%$l2$u3$p3%q3%q4%r4%+&&8)&V.&g2&r4&t4&t5&u5&.('O/'t5'u5'v5'u6'v6'q6(v7(y7(o7)}9),+*V2*g4*x9*:.,x;,/..70.C2.z=/211u=1l=2z@3666X?9:::}E:><<PB>~I>AAALAlKCODEEERFIIIUIUJKKK`OKMMMZPRRR\TS^TUUU_WaWYYYs_Zt`[`^^__^g_a``bbaccceeeofhggiiillksknmmpppxptsruuuxvxxxyyy}{y}z{{{|}}}~}~¿ȏՎV)IzEl~zEzEtgzE{=?bzEM+$0LqzE9!$ 8YzE5$CdzEc/$0LtzEW$$ :ZzEG$!Ce|?>$$0Ovo*R}u7$:Zc5 ?ac&!CeD $*JmN$$0R\%$ 4Rxf,! .eF <]v=)6u7!$EeږM'#(Tc-#0Ot׽a+ !AQ$# 8Yvh=$$6jB$ 1!!%Ty7!!!"A`&$6jQ&"%T@$Ay7!$6j`&!%SQ'$A?????traits-4.6.0/docs/source/.templates/000077500000000000000000000000001300633736300173375ustar00rootroot00000000000000traits-4.6.0/docs/source/.templates/layout.html000066400000000000000000000003631300633736300215440ustar00rootroot00000000000000{# Filename: .templates/layout.html #} {% extends '!layout.html' %} {% block relbaritems %} {% if current_page_name != 'index' %}
  • {{ title }}
  • {% endif %} {% endblock %} traits-4.6.0/docs/source/.templates/search.html000066400000000000000000000017451300633736300215010ustar00rootroot00000000000000{% extends "!search.html" %} {% block body %}

    Search

    Enter your search words into the box below and click search. Note that the search function automatically searches for all of the words. Pages containing some but not all of them won't appear in the result list.

    {% if search_performed %}

    Search Results

    {% if not search_results %}

    Your search did not match any results.

    {% endif %} {% endif %}
    {% if search_results %}
      {% for href, caption, context in search_results %}
    • {{ caption }}
      {{ context|e }}
    • {% endfor %}
    {% endif %}
    {% endblock %} traits-4.6.0/docs/source/_extensions/000077500000000000000000000000001300633736300176215ustar00rootroot00000000000000traits-4.6.0/docs/source/_extensions/__init__.py000066400000000000000000000000001300633736300217200ustar00rootroot00000000000000traits-4.6.0/docs/source/_extensions/refactordoc/000077500000000000000000000000001300633736300221145ustar00rootroot00000000000000traits-4.6.0/docs/source/_extensions/refactordoc/LICENSE.txt000066400000000000000000000031201300633736300237330ustar00rootroot00000000000000This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. Copyright (c) 2006, Enthought, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Enthought, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. traits-4.6.0/docs/source/_extensions/refactordoc/__init__.py000066400000000000000000000016621300633736300242320ustar00rootroot00000000000000#------------------------------------------------------------------------------ # file: refactor_doc.py # License: LICENSE.TXT # # Copyright (c) 2011, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ from .function_doc import FunctionDoc from .class_doc import ClassDoc #------------------------------------------------------------------------------ # Extension definition #------------------------------------------------------------------------------ def refactor_docstring(app, what, name, obj, options, lines): refactor = None if ('class' in what): refactor = ClassDoc(lines) elif ('function' in what) or ('method' in what): refactor = FunctionDoc(lines) if refactor is not None: refactor.parse() def setup(app): app.setup_extension('sphinx.ext.autodoc') app.connect('autodoc-process-docstring', refactor_docstring) traits-4.6.0/docs/source/_extensions/refactordoc/base_doc.py000066400000000000000000000274261300633736300242400ustar00rootroot00000000000000# -*- coding: utf-8 -*- #------------------------------------------------------------------------------ # file: base_doc.py # License: LICENSE.TXT # # Copyright (c) 2011, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ import re from definition_items import DefinitionItem from line_functions import is_empty, get_indent, fix_backspace, NEW_LINE underline_regex = re.compile(r'\s*\S+\s*\Z') #------------------------------------------------------------------------------ # Classes #------------------------------------------------------------------------------ class BaseDoc(object): """Base abstract docstring refactoring class. The class' main purpose is to parse the dosctring and find the sections that need to be refactored. Subclasses should provide the methods responsible for refactoring the sections. Attributes ---------- docstring : list A list of strings (lines) that holds docstrings index : int The current zero-based line number of the docstring that is currently proccessed. headers : dict The sections that the class refactors. Each entry in the dictionary should have as key the name of the section in the form that it appears in the docstrings. The value should be the postfix of the method, in the subclasses, that is responsible for refactoring (e.g. {'Methods': 'method'}). BaseDoc also provides a number of methods that operate on the docstring to help with the refactoring. This is neccessary because the docstring has to change inplace and thus it is better to live the docstring manipulation to the class methods instead of accessing the lines directly. """ def __init__(self, lines, headers=None): """ Initialize the class The method setups the class attributes and starts parsing the docstring to find and refactor the sections. Arguments --------- lines : list of strings The docstring to refactor headers : dict The sections for which the class has custom refactor methods. Each entry in the dictionary should have as key the name of the section in the form that it appears in the docstrings. The value should be the postfix of the method, in the subclasses, that is responsible for refactoring (e.g. {'Methods': 'method'}). """ try: self._docstring = lines.splitlines() except AttributeError: self._docstring = lines self.headers = {} if headers is None else headers self.bookmarks = [] def parse(self): """ Parse the docstring. The docstring is parsed for sections. If a section is found then the corresponding refactoring method is called. """ self.index = 0 self.seek_to_next_non_empty_line() while not self.eod: header = self.is_section() if header: self._refactor(header) else: self.index += 1 self.seek_to_next_non_empty_line() def _refactor(self, header): """Call the heading refactor method. The header is removed from the docstring and the docstring refactoring is dispatched to the appropriate refactoring method. The name of the refctoring method is constructed using the form _refactor_
    . Where
    is the value corresponding to ``self.headers[header]``. If there is no custom method for the section then the self._refactor_header() is called with the found header name as input. """ self.remove_lines(self.index, 2) # Remove header self.remove_if_empty(self.index) # Remove space after header refactor_postfix = self.headers.get(header, 'header') method_name = ''.join(('_refactor_', refactor_postfix)) method = getattr(self, method_name) lines = method(header) self.insert_and_move(lines, self.index) def _refactor_header(self, header): """ Refactor the header section using the rubric directive. The method has been tested and supports refactoring single word headers, two word headers and headers that include a backslash ''\''. Arguments --------- header : string The header string to use with the rubric directive. """ header = fix_backspace(header) directive = '.. rubric:: {0}'.format(header) lines = [] lines += [directive, NEW_LINE] return lines def extract_items(self, item_class=None): """ Extract the definition items from a docstring. Parse the items in the description of a section into items of the provided class time. Given a DefinitionItem or a subclass defined by the ``item_class`` parameter. Staring from the current index position, the method checks if in the next two lines a valid header exists. If successful, then the lines that belong to the item description block (i.e. header + definition) are poped put from the docstring and passed to the ``item_class`` parser and create an instance of ``item_class``. The process is repeated until there is no compatible ``item_class`` found or we run out of docstring. Then the method returns a list of item_class instances. The exit conditions allow for two valid section item layouts: 1. No lines between items:: 2. One line between items:: Arguments --------- item_class : DefinitionItem A DefinitionItem or a subclass. This argument is used to check if a line in the docstring is a valid item and to parse the individual list items in the section. When ``None`` (default) the base DefinitionItem class is used. Returns ------- parameters : list List of the parsed item instances of ``item_class`` type. """ item_type = DefinitionItem if (item_class is None) else item_class is_item = item_type.is_definition item_blocks = [] while (not self.eod) and \ (is_item(self.peek()) or is_item(self.peek(1))): self.remove_if_empty(self.index) item_blocks.append(self.get_next_block()) items = [item_type.parse(block) for block in item_blocks] return items def get_next_block(self): """ Get the next item block from the docstring. The method reads the next item block in the docstring. The first line is assumed to be the DefinitionItem header and the following lines to belong to the definition::
    The end of the field is designated by a line with the same indent as the field header or two empty lines are found in sequence. """ item_header = self.pop() sub_indent = get_indent(item_header) + ' ' block = [item_header] while (not self.eod): peek_0 = self.peek() peek_1 = self.peek(1) if (is_empty(peek_0) and (not peek_1.startswith(sub_indent))) \ or ((not is_empty(peek_0)) \ and (not peek_0.startswith(sub_indent))): break else: line = self.pop() block += [line.rstrip()] return block def is_section(self): """ Check if the current line defines a section. .. todo:: split and cleanup this method. """ if self.eod: return False header = self.peek() line2 = self.peek(1) # check for underline type format underline = underline_regex.match(line2) if underline is None: return False # is the nextline an rst underline? striped_header = header.rstrip() expected_underline1 = re.sub(r'[A-Za-z\\]|\b\s', '-', striped_header) expected_underline2 = re.sub(r'[A-Za-z\\]|\b\s', '=', striped_header) if ((underline.group().rstrip() == expected_underline1) or (underline.group().rstrip() == expected_underline2)): return header.strip() else: return False def insert_lines(self, lines, index): """ Insert refactored lines Arguments --------- new_lines : list The list of lines to insert index : int Index to start the insertion """ docstring = self.docstring for line in reversed(lines): docstring.insert(index, line) def insert_and_move(self, lines, index): """ Insert refactored lines and move current index to the end. """ self.insert_lines(lines, index) self.index += len(lines) def seek_to_next_non_empty_line(self): """ Goto the next non_empty line. """ docstring = self.docstring for line in docstring[self.index:]: if not is_empty(line): break self.index += 1 def get_next_paragraph(self): """ Get the next paragraph designated by an empty line. """ lines = [] while (not self.eod) and (not is_empty(self.peek())): line = self.pop() lines.append(line) return lines def read(self): """ Return the next line and advance the index. """ index = self.index line = self._docstring[index] self.index += 1 return line def remove_lines(self, index, count=1): """ Removes the lines from the docstring """ docstring = self.docstring del docstring[index:(index + count)] def remove_if_empty(self, index=None): """ Remove the line from the docstring if it is empty. """ if is_empty(self.docstring[index]): self.remove_lines(index) def bookmark(self): """ append the current index to the end of the list of bookmarks. """ self.bookmarks.append(self.index) def goto_bookmark(self, bookmark_index=-1): """ Move to bookmark. Move the current index to the docstring line given my the ``self.bookmarks[bookmark_index]`` and remove it fromn the bookmark list. Default value will pop the last entry. Returns ------- bookmark : int """ self.index = self.bookmarks[bookmark_index] return self.bookmarks.pop(bookmark_index) def peek(self, ahead=0): """ Peek ahead a number of lines The function retrieves the line that is ahead of the current index. If the index is at the end of the list then it returns an empty string. Arguments --------- ahead : int The number of lines to look ahead. """ position = self.index + ahead try: line = self.docstring[position] except IndexError: line = '' return line def pop(self, index=None): """ Pop a line from the dostrings. """ index = self.index if (index is None) else index return self._docstring.pop(index) @property def eod(self): """ End of docstring. """ return self.index >= len(self.docstring) @property def docstring(self): """ Get the docstring lines. """ return self._docstring traits-4.6.0/docs/source/_extensions/refactordoc/class_doc.py000066400000000000000000000120061300633736300244170ustar00rootroot00000000000000# -*- coding: UTF-8 -*- #------------------------------------------------------------------------------ # file: class_doc.py # License: LICENSE.TXT # # Copyright (c) 2011, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ from base_doc import BaseDoc from line_functions import get_indent, replace_at, add_indent from definition_items import (MethodItem, AttributeItem, TableLineItem, max_attribute_length, max_attribute_index, ListItem) class ClassDoc(BaseDoc): """ Docstring refactoring for classes. The class provides the following refactoring methods. Methods ------- _refactor_attributes(self, header) : Refactor the attributes section to sphinx friendly format. _refactor_methods(self, header) : Refactor the methods section to sphinx friendly format. _refactor_as_items_list(self, header) : Refactor the Keywords section to sphinx friendly format. _refactor_notes(self, header) : Refactor the note section to use the rst ``.. note`` directive. _refactor_example(self, header) : Refactor the example section to sphinx friendly format. """ def __init__(self, lines, headers=None): if headers is None: headers = {'Attributes': 'attributes', 'Methods': 'methods', 'Notes': 'notes', 'Keywords': 'as_item_list', 'Note': 'notes', 'Example': 'example', 'Examples': 'example'} super(ClassDoc, self).__init__(lines, headers) return def _refactor_attributes(self, header): """Refactor the attributes section to sphinx friendly format""" items = self.extract_items(AttributeItem) lines = [] for item in items: lines += item.to_rst() return lines def _refactor_methods(self, header): """Refactor the methods section to sphinx friendly format. """ items = self.extract_items(MethodItem) lines = [] if len(items) > 0 : columns = self._get_column_lengths(items) border = '{0:=^{1}} {0:=^{2}}'.format('', columns[0], columns[1]) heading = '{0:<{2}} {1:<{3}}'.format('Method', 'Description', columns[0], columns[1]) lines += [border] lines += [heading] lines += [border] for items in items: lines += items.to_rst(columns) lines += [border] lines += [''] lines = [line.rstrip() for line in lines] return lines def _refactor_notes(self, header): """Refactor the note section to use the rst ``.. note`` directive. """ paragraph = self.get_next_paragraph() lines = ['.. note::'] lines += add_indent(paragraph) return lines def _refactor_as_item_list(self, header): """ Refactor the a section to sphinx friendly item list. Arguments --------- header : str The header name that is used for the fields (i.e. ``:
    :``). """ items = self.extract_items(item_class=ListItem) lines = [':{0}:'.format(header.lower()), ''] prefix = None if len(items) == 1 else '-' for item in items: lines += add_indent(item.to_rst(prefix)) return lines def _refactor_example(self, header) : """ Refactor the example section to sphinx friendly format. Arguments --------- header : str The header name that is used for the fields (i.e. ``:
    :``). """ paragraph = self.get_next_paragraph() lines = ['.. rubric:: {0}'.format(header), '', '::', ''] lines += add_indent(paragraph) return lines def _get_column_lengths(self, items): """ Helper function to estimate the column widths for the refactoring of the ``Methods`` section. The method finds the index of the item that has the largest function name (i.e. self.term) and the largest signature. If the indexes are not the same then checks to see which of the two items have the largest string sum (i.e. self.term + self.signature). """ name_index = max_attribute_index(items, 'term') signature_index = max_attribute_index(items, 'signature') if signature_index != name_index: index = signature_index item1_width = len(items[index].term + items[index].signature) index = name_index item2_width = len(items[index].term + items[index].signature) first_column = max(item1_width, item2_width) else: index = name_index first_column = len(items[index].term + items[index].signature) first_column += 11 # Add boilerplate characters second_column = max_attribute_length(items, 'definition') return (first_column, second_column) traits-4.6.0/docs/source/_extensions/refactordoc/definition_items.py000066400000000000000000000410131300633736300260160ustar00rootroot00000000000000# -*- coding: utf-8 -*- #----------------------------------------------------------------------------- # file: fields.py # License: LICENSE.TXT # Author: Ioannis Tziakos # # Copyright (c) 2011, Enthought, Inc. # All rights reserved. #----------------------------------------------------------------------------- import collections import re from line_functions import (add_indent, fix_star, trim_indent, NEW_LINE, fix_trailing_underscore) header_regex = re.compile(r'\s:\s?') definition_regex = re.compile(r""" \*{0,2} # no, one or two stars \w+\s: # a word followed by a space and a semicolumn (.+)? # a definition $ # match at the end of the line """, re.VERBOSE) function_regex = re.compile(r'\w+\(.*\)\s*') signature_regex = re.compile('\((.*)\)') class DefinitionItem(collections.namedtuple( 'DefinitionItem', ('term', 'classifier', 'definition'))): """ A docstring definition item Syntax diagram:: +-------------------------------------------------+ | term [ " : " classifier [ " or " classifier] ] | +--+----------------------------------------------+---+ | definition | | (body elements)+ | +--------------------------------------------------+ The Definition class is based on the nametuple class and is responsible to check, parse and refactor a docstring definition item into sphinx friendly rst. Attributes ---------- term : str The term usually reflects the name of a parameter or an attribute. classifier: str The classifier of the definition. Commonly used to reflect the type of an argument or the signature of a function. .. note:: Currently only one classifier is supported. definition : list The list of strings that holds the description the definition item. .. note:: A Definition item is based on the item of a section definition list as it defined in restructured text (_http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections). """ @classmethod def is_definition(cls, line): """ Check if the line is describing a definition item. The method is used to check that a line is following the expected format for the term and classifier attributes. The expected format is:: +-------------------------------------------------+ | term [ " : " classifier [ " or " classifier] ] | +-------------------------------------------------+ Subclasses can subclass to restrict or expand this format. """ return definition_regex.match(line) is not None @classmethod def parse(cls, lines): """Parse a definition item from a set of lines. The class method parses the definition list item from the list of docstring lines and produces a DefinitionItem with the term, classifier and the definition. .. note:: The global indention in the definition lines is striped The term definition is assumed to be in one of the following formats:: term Definition. :: term Definition, paragraph 1. Definition, paragraph 2. :: term : classifier Definition. Arguments --------- lines docstring lines of the definition without any empty lines before or after. Returns ------- definition : DefinitionItem """ header = lines[0].strip() term, classifier = header_regex.split(header, maxsplit=1) if \ (' :' in header) else (header, '') trimed_lines = trim_indent(lines[1:]) if (len(lines) > 1) else [''] definition = [line.rstrip() for line in trimed_lines] return cls(term.strip(), classifier.strip(), definition) def to_rst(self, **kwards): """ Outputs the Definition in sphinx friendly rst. The method renders the definition into a list of lines that follow the rst markup. The default behaviour is to render the definition as an sphinx definition item:: () -- Subclasses will usually override the method to provide custom made behaviour. However the signature of the method should hold only keyword arguments which have default values. The keyword arguments can be used to pass addition rendering information to subclasses. Returns ------- lines : list A list of string lines rendered in rst. Example ------- :: >>> item = DefinitionItem('lines', 'list', ['A list of string lines rendered in rst.']) >>> item.to_rst() lines *(list)* -- A list of string lines rendered in rst. .. note:: An empty line is added at the end of the list of strings so that the results can be concatenated directly and rendered properly by sphinx. """ postfix = ' --' if (len(self.definition) > 0) else '' lines = [] lines += [self.term] lines += [NEW_LINE] lines += [' *({0})*{1}'.format(self.classifier, postfix)] lines += add_indent(self.definition) # definition is all ready a list lines += [NEW_LINE] return lines class AttributeItem(DefinitionItem): """ Definition that renders the rst output using the attribute directive. """ _normal = (".. attribute:: {0}\n" " :annotation: = {1}\n" "\n" "{2}\n\n") _no_definition = (".. attribute:: {0}\n" " :annotation: = {1}\n\n") _no_classifier = (".. attribute:: {0}\n\n" "{2}\n\n") _only_term = ".. attribute:: {0}\n\n" def to_rst(self, ): """ Return the attribute info using the attribute sphinx markup. Examples -------- :: >>> item = AttributeItem('indent', 'int', ... ['The indent to use for the description block.']) >>> item.to_rst() .. attribute:: indent :annotation: = int The indent to use for the description block >>> :: >>> item = AttributeItem('indent', '', ... ['The indent to use for the description block.']) >>> item.to_rst() .. attribute:: indent The indent to use for the description block >>> .. note:: An empty line is added at the end of the list of strings so that the results can be concatenated directly and rendered properly by sphinx. """ definition = '\n'.join(add_indent(self.definition)) template = self.template.format(self.term, self.classifier, definition) return template.splitlines() @property def template(self): if self.classifier == '' and self.definition == ['']: template = self._only_term elif self.classifier == '': template = self._no_classifier elif self.definition == ['']: template = self._no_definition else: template = self._normal return template class ArgumentItem(DefinitionItem): """ A definition item for function argument sections. """ _normal = (":param {0}:\n" "{2}\n" ":type {0}: {1}") _no_definition = (":param {0}:\n" ":type {0}: {1}") _no_classifier = (":param {0}:\n" "{2}") _only_term = ":param {0}:" def to_rst(self): """ Render ArgumentItem in sphinx friendly rst using the ``:param:`` role. Example ------- :: >>> item = ArgumentItem('indent', 'int', ... ['The indent to use for the description block.', '' 'This is the second paragraph of the argument definition.']) >>> item.to_rst() :param indent: The indent to use for the description block. This is the second paragraph of the argument definition. :type indent: int .. note:: There is no new line added at the last line of the :meth:`to_rst` method. """ argument = fix_star(self.term) argument = fix_trailing_underscore(argument) argument_type = self.classifier definition = '\n'.join(add_indent(self.definition)) template = self.template.format(argument, argument_type, definition) return template.splitlines() @property def template(self): if self.classifier == '' and self.definition == ['']: template = self._only_term elif self.classifier == '': template = self._no_classifier elif self.definition == ['']: template = self._no_definition else: template = self._normal return template class ListItem(DefinitionItem): """ A definition item that is rendered as an ordered/unordered list """ _normal = ("**{0}** (*{1}*) --\n" "{2}\n\n") _only_term = "**{0}**\n\n" _no_definition = "**{0}** (*{1}*)\n\n" _no_classifier = ("**{0}** --\n" "{2}\n\n") def to_rst(self, prefix=None): """ Outputs ListItem in rst using as items in an list. Arguments --------- prefix : str The prefix to use. For example if the item is part of a numbered list then ``prefix='-'``. Example ------- >>> item = ListItem('indent', 'int', ... ['The indent to use for the description block.']) >>> item.to_rst(prefix='-') - **indent** (`int`) -- The indent to use for the description block. >>> item = ListItem('indent', 'int', ... ['The indent to use for' 'the description block.']) >>> item.to_rst(prefix='-') - **indent** (`int`) -- The indent to use for the description block. .. note:: An empty line is added at the end of the list of strings so that the results can be concatenated directly and rendered properly by sphinx. """ indent = 0 if (prefix is None) else len(prefix) + 1 definition = '\n'.join(add_indent(self.definition, indent)) template = self.template.format(self.term, self.classifier, definition) if prefix is not None: template = prefix + ' ' + template return template.splitlines() @property def template(self): if self.classifier == '' and self.definition == ['']: template = self._only_term elif self.classifier == '': template = self._no_classifier elif self.definition == ['']: template = self._no_definition else: template = self._normal return template class TableLineItem(DefinitionItem): """ A Definition Item that represents a table line. """ def to_rst(self, columns=(0, 0, 0)): """ Outputs definition in rst as a line in a table. Arguments --------- columns : tuple The three item tuple of column widths for the term, classifier and definition fields of the TableLineItem. When the column width is 0 then the field .. note:: - The strings attributes are clipped to the column width. Example ------- >>> item = TableLineItem('function(arg1, arg2)', '', ... ['This is the best function ever.']) >>> item.to_rst(columns=(22, 0, 20)) function(arg1, arg2) This is the best fun """ definition = ' '.join([line.strip() for line in self.definition]) term = self.term[:columns[0]] classifier = self.classifier[:columns[1]] definition = definition[:columns[2]] first_column = '' if columns[0] == 0 else '{0:<{first}} ' second_column = '' if columns[1] == 0 else '{1:<{second}} ' third_column = '' if columns[2] == 0 else '{2:<{third}}' table_line = ''.join((first_column, second_column, third_column)) lines = [] lines += [table_line.format(term, classifier, definition, first=columns[0], second=columns[1], third=columns[2])] lines += [''] return lines class MethodItem(DefinitionItem): """ A TableLineItem subclass to parse and render class methods. """ @classmethod def is_definition(cls, line): """ Check if the definition header is a function signature. """ match = function_regex.match(line) return match @classmethod def parse(cls, lines): """Parse a method definition item from a set of lines. The class method parses the method signature and definition from the list of docstring lines and produces a MethodItem where the term is the method name and the classifier is arguments .. note:: The global indention in the definition lines is striped The method definition item is assumed to be as follows:: +------------------------------+ | term "(" [ classifier ] ")" | +--+---------------------------+---+ | definition | | (body elements)+ | +--------------------- ---------+ Arguments --------- lines : docstring lines of the method definition item without any empty lines before or after. Returns ------- definition : MethodItem """ header = lines[0].strip() term, classifier, _ = signature_regex.split(header) definition = trim_indent(lines[1:]) if (len(lines) > 1) else [''] return cls(term, classifier, definition) def to_rst(self, columns=(0, 0)): """ Outputs definition in rst as a line in a table. Arguments --------- columns : tuple The two item tuple of column widths for the :meth: role column and the definition (i.e. summary) of the MethodItem .. note:: The strings attributes are clipped to the column width. Example ------- :: >>> item = MethodItem('function', 'arg1, arg2', ... ['This is the best function ever.']) >>> item.to_rst(columns=(40, 20)) :meth:`function ` This is the best fun """ definition = ' '.join([line.strip() for line in self.definition]) method_role = ':meth:`{0}({1}) <{0}>`'.format(self.term, self.classifier) table_line = '{0:<{first}} {1:<{second}}' lines = [] lines += [table_line.format(method_role[:columns[0]], definition[:columns[1]], first=columns[0], second=columns[1])] return lines @property def signature(self): return '{}({})'.format(self.term, self.classifier) #------------------------------------------------------------------------------ # Functions to work with Definition Items #------------------------------------------------------------------------------ def max_attribute_length(items, attr): """ Find the max length of the attribute in a list of DefinitionItems. Arguments --------- items : list The list of the DefinitionItem instances (or subclasses). attr : str Attribute to look at. """ if attr == 'definition': maximum = max([len(' '.join(item.definition)) for item in items]) else: maximum = max([len(getattr(item, attr)) for item in items]) return maximum def max_attribute_index(items, attr): """ Find the index of the attribute with the maximum length in a list of DefinitionItems. Arguments --------- items : list The list of the DefinitionItems (or subclasses). attr : str Attribute to look at. """ if attr == 'definition': attributes = [len(' '.join(item.definition)) for item in items] else: attributes = [len(getattr(item, attr)) for item in items] maximum = max(attributes) return attributes.index(maximum) traits-4.6.0/docs/source/_extensions/refactordoc/function_doc.py000066400000000000000000000054231300633736300251440ustar00rootroot00000000000000# -*- coding: UTF-8 -*- #------------------------------------------------------------------------------ # file: function_doc.py # License: LICENSE.TXT # Author: Ioannis Tziakos # # Copyright (c) 2011, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ from base_doc import BaseDoc from line_functions import get_indent, add_indent from definition_items import ArgumentItem, ListItem class FunctionDoc(BaseDoc): """Docstring refactoring for functions The class provides the following refactoring methods. Methods ------- _refactor_arguments(self, header) : Refactor the Arguments and Parameters section to sphinx friendly format. _refactor_as_items_list(self, header) : Refactor the Returns, Raises and Yields sections to sphinx friendly format. _refactor_notes(self, header) : Refactor the note section to use the rst ``.. note`` directive. """ def __init__(self, lines, headers=None): if headers is None: headers = {'Returns': 'as_item_list', 'Arguments': 'arguments', 'Parameters': 'arguments', 'Raises': 'as_item_list', 'Yields': 'as_item_list', 'Notes':'notes', 'Note': 'notes'} super(FunctionDoc, self).__init__(lines, headers) return def _refactor_as_item_list(self, header): """ Refactor the a section to sphinx friendly item list. Arguments --------- header : str The header name that is used for the fields (i.e. ``:
    :``). """ items = self.extract_items(item_class=ListItem) lines = [':{0}:'.format(header.lower())] if len(items) > 0: prefix = None if len(items) == 1 else '-' for item in items: lines += add_indent(item.to_rst(prefix)) else: paragraph = self.get_next_paragraph() lines += add_indent(paragraph) return lines def _refactor_arguments(self, header): """ Refactor the argument section to sphinx friendly format. Arguments --------- header : unused This parameter is ingnored in thi method. """ items = self.extract_items(item_class=ArgumentItem) lines = [] for item in items: lines += item.to_rst() return lines def _refactor_notes(self, header): """ Refactor the notes section to sphinx friendly format. Arguments --------- header : unused This parameter is ingnored in this method. """ paragraph = self.get_next_paragraph() lines = ['.. note::'] lines += add_indent(paragraph) return lines traits-4.6.0/docs/source/_extensions/refactordoc/line_functions.py000066400000000000000000000075001300633736300255070ustar00rootroot00000000000000# -*- coding: utf-8 -*- #----------------------------------------------------------------------------- # file: line_functions.py # License: LICENSE.TXT # Author: Ioannis Tziakos # # Copyright (c) 2011, Enthought, Inc. # All rights reserved. #----------------------------------------------------------------------------- import re #----------------------------------------------------------------------------- # Pre-compiled regexes #----------------------------------------------------------------------------- indent_regex = re.compile(r'\s+') #----------------------------------------------------------------------------- # Constants #----------------------------------------------------------------------------- NEW_LINE = '' #------------------------------------------------------------------------------ # Functions to manage indentation #------------------------------------------------------------------------------ def add_indent(lines, indent=4): """ Add spaces to indent a list of lines. Arguments --------- lines : list The list of strings to indent. indent : int The number of spaces to add. Returns ------- lines : list The indented strings (lines). Notes ----- Empty strings are not changed. """ indent_str = ' ' * indent if indent != 0 else '' output = [] for line in lines: if is_empty(line): output.append(line) else: output.append(indent_str + line) return output def remove_indent(lines): """ Remove all indentation from the lines. Returns ------- result : list A new list of left striped strings. """ return [line.lstrip() for line in lines] def trim_indent(lines): """ Trim global indentation level from lines. """ non_empty_lines = filter(lambda x: not is_empty(x), lines) indent = set(len(get_indent(line)) for line in non_empty_lines) indent.discard(0) global_indent = min(indent) return [line[global_indent:] for line in lines] def get_indent(line): """ Return the indent portion of the line. """ indent = indent_regex.match(line) if indent is None: return '' else: return indent.group() #------------------------------------------------------------------------------ # Functions to detect line type #------------------------------------------------------------------------------ def is_empty(line): return not line.strip() #------------------------------------------------------------------------------ # Functions to adjust strings #------------------------------------------------------------------------------ def fix_star(word): """ Replace ``*`` with ``\*`` so that is will be parse properly by docutils. """ return word.replace('*', '\*') def fix_backspace(word): """ Replace ``\\`` with ``\\\\`` so that it will printed properly in the documentation. """ return word.replace('\\', '\\\\') def fix_trailing_underscore(word): """ Replace the trailing ``_`` with ``\\_`` so that it will printed properly in the documentation. """ if word.endswith('_'): word = word.replace('_', '\_') return word def replace_at(word, line, index): """ Replace the text in-line. The text in line is replaced (not inserted) with the word. The replacement starts at the provided index. The result is cliped to the input length Arguments --------- word : str The text to copy into the line. line : str The line where the copy takes place. index : int The index to start coping. Returns ------- result : str line of text with the text replaced. """ word_length = len(word) result = line[:index] + word + line[(index + word_length):] return result[:len(line)] traits-4.6.0/docs/source/_static/000077500000000000000000000000001300633736300167115ustar00rootroot00000000000000traits-4.6.0/docs/source/_static/default.css000066400000000000000000000323121300633736300210500ustar00rootroot00000000000000/** * Sphinx Doc Design */ body { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; font-size: 100%; background-color: #333333; color: #000; margin: 0; padding: 0; } /* :::: LAYOUT :::: */ div.document { background-color: #24326e; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: white; padding: 0 20px 30px 20px; } div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } p.logo { text-align: center; } div.clearer { clear: both; } div.footer { color: #fff; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: #fff; text-decoration: underline; } div.related { background-color: #24326e; color: #fff; width: 100%; height: 30px; line-height: 30px; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } div.related a { color: white; } /* ::: TOC :::: */ div.sphinxsidebar h3 { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; color: #acafb3; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h4 { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; color: #acafb3; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: white; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; list-style: none; color: white; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar a { color: #fff; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #9bbde2; font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; font-size: 1em; } /* :::: MODULE CLOUD :::: */ div.modulecloud { margin: -5px 10px 5px 10px; padding: 10px; line-height: 160%; border: 1px solid #666666; background-color: #dddddd; } div.modulecloud a { padding: 0 5px 0 5px; } /* :::: SEARCH :::: */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #666; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* :::: COMMON FORM STYLES :::: */ div.actions { padding: 5px 10px 5px 10px; border-top: 1px solid #598ec0; border-bottom: 1px solid #598ec0; background-color: #9bbde2; } form dl { color: #333; } form dt { clear: both; float: left; min-width: 110px; margin-right: 10px; padding-top: 2px; } input#homepage { display: none; } div.error { margin: 5px 20px 0 0; padding: 5px; border: 1px solid #db7d46; font-weight: bold; } /* :::: INLINE COMMENTS :::: */ div.inlinecomments { position: absolute; right: 20px; } div.inlinecomments a.bubble { display: block; float: right; background-image: url(style/comment.png); background-repeat: no-repeat; width: 25px; height: 25px; text-align: center; padding-top: 3px; font-size: 0.9em; line-height: 14px; font-weight: bold; color: black; } div.inlinecomments a.bubble span { display: none; } div.inlinecomments a.emptybubble { background-image: url(style/nocomment.png); } div.inlinecomments a.bubble:hover { background-image: url(style/hovercomment.png); text-decoration: none; color: #598ec0; } div.inlinecomments div.comments { float: right; margin: 25px 5px 0 0; max-width: 50em; min-width: 30em; border: 1px solid #598ec0; background-color: #9bbde2; z-index: 150; } div#comments { border: 1px solid #598ec0; margin-top: 20px; } div#comments div.nocomments { padding: 10px; font-weight: bold; } div.inlinecomments div.comments h3, div#comments h3 { margin: 0; padding: 0; background-color: #598ec0; color: white; border: none; padding: 3px; } div.inlinecomments div.comments div.actions { padding: 4px; margin: 0; border-top: none; } div#comments div.comment { margin: 10px; border: 1px solid #598ec0; } div.inlinecomments div.comment h4, div.commentwindow div.comment h4, div#comments div.comment h4 { margin: 10px 0 0 0; background-color: #2eabb0; color: white; border: none; padding: 1px 4px 1px 4px; } div#comments div.comment h4 { margin: 0; } div#comments div.comment h4 a { color: #9bbde2; } div.inlinecomments div.comment div.text, div.commentwindow div.comment div.text, div#comments div.comment div.text { margin: -5px 0 -5px 0; padding: 0 10px 0 10px; } div.inlinecomments div.comment div.meta, div.commentwindow div.comment div.meta, div#comments div.comment div.meta { text-align: right; padding: 2px 10px 2px 0; font-size: 95%; color: #598ec0; border-top: 1px solid #598ec0; background-color: #9bbde2; } div.commentwindow { position: absolute; width: 500px; border: 1px solid #598ec0; background-color: #9bbde2; display: none; z-index: 130; } div.commentwindow h3 { margin: 0; background-color: #598ec0; color: white; border: none; padding: 5px; font-size: 1.5em; cursor: pointer; } div.commentwindow div.actions { margin: 10px -10px 0 -10px; padding: 4px 10px 4px 10px; color: #598ec0; } div.commentwindow div.actions input { border: 1px solid #598ec0; background-color: white; color: #073d61; cursor: pointer; } div.commentwindow div.form { padding: 0 10px 0 10px; } div.commentwindow div.form input, div.commentwindow div.form textarea { border: 1px solid #598ec0; background-color: white; color: black; } div.commentwindow div.error { margin: 10px 5px 10px 5px; background-color: #fff2b0; display: none; } div.commentwindow div.form textarea { width: 99%; } div.commentwindow div.preview { margin: 10px 0 10px 0; background-color: ##9bbde2; padding: 0 1px 1px 25px; } div.commentwindow div.preview h4 { margin: 0 0 -5px -20px; padding: 4px 0 0 4px; color: white; font-size: 1.3em; } div.commentwindow div.preview div.comment { background-color: #f2fbfd; } div.commentwindow div.preview div.comment h4 { margin: 10px 0 0 0!important; padding: 1px 4px 1px 4px!important; font-size: 1.2em; } /* :::: SUGGEST CHANGES :::: */ div#suggest-changes-box input, div#suggest-changes-box textarea { border: 1px solid #666; background-color: white; color: black; } div#suggest-changes-box textarea { width: 99%; height: 400px; } /* :::: PREVIEW :::: */ div.preview { background-image: url(style/preview.png); padding: 0 20px 20px 20px; margin-bottom: 30px; } /* :::: INDEX PAGE :::: */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* :::: INDEX STYLES :::: */ table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #dddddd; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } form.pfform { margin: 10px 0 20px 0; } /* :::: GLOBAL STYLES :::: */ .docwarning { background-color: #fff2b0; padding: 10px; margin: 0 -20px 0 -20px; border-bottom: 1px solid #db7d46; } p.subhead { font-weight: bold; margin-top: 20px; } a { color: #24326e; text-decoration: none; } a:hover { text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; background-color: #dddddd; font-weight: normal; color: #073d61; border-bottom: 1px solid #666; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #edaa1e; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } a.headerlink:hover { background-color: #edaa1e; color: white; } div.body p, div.body dd, div.body li { text-align: left; line-height: 130%; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } ul.fakelist { list-style: none; margin: 10px 0 10px 20px; padding: 0; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } /* "Footnotes" heading */ p.rubric { margin-top: 30px; font-weight: bold; } /* "Topics" */ div.topic { background-color: #ddd; border: 1px solid #666; padding: 0 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* Admonitions */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } div.admonition p { display: inline; } div.seealso { background-color: #fff2b0; border: 1px solid #edaa1e; } div.warning { background-color: #fff2b0; border: 1px solid ##db7d46; } div.note { background-color: #eee; border: 1px solid #666; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; display: inline; } p.admonition-title:after { content: ":"; } div.body p.centered { text-align: center; margin-top: 25px; } table.docutils { border: 0; } table.docutils td, table.docutils th { padding: 1px 8px 1px 0; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #a9a6a2; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } dl { margin-bottom: 15px; clear: both; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } .refcount { color: #24326e; } dt:target, .highlight { background-color: #edaa1e1; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } th { text-align: left; padding-right: 5px; } pre { padding: 5px; background-color: #e6f3ff; color: #333; border: 1px solid #24326e; border-left: none; border-right: none; overflow: auto; } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt { background-color: #ddd; padding: 0 1px 0 1px; font-size: 1.2em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } .footnote:target { background-color: #fff2b0 } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } form.comment { margin: 0; padding: 10px 30px 10px 30px; background-color: #ddd; } form.comment h3 { background-color: #598ec0; color: white; margin: -10px -30px 10px -30px; padding: 5px; font-size: 1.4em; } form.comment input, form.comment textarea { border: 1px solid #ddd; padding: 2px; font-family: 'Verdana', 'Helvetica', 'Arial', sans-serif; font-size: 100%; } form.comment input[type="text"] { width: 240px; } form.comment textarea { width: 100%; height: 200px; margin-bottom: 10px; } .system-message { background-color: #edaa1e; padding: 5px; border: 3px solid red; } /* :::: PRINT :::: */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0; width : 100%; } div.sphinxsidebar, div.related, div.footer, div#comments div.new-comment-box, #top-link { display: none; } } traits-4.6.0/docs/source/_static/e-logo-rev.png000066400000000000000000000075111300633736300213770ustar00rootroot00000000000000PNG  IHDRoi*sRGB pHYs  tIMELIDATx]ktT>z\$m ȥ!TD jV|_CWph PT\"B$$6aIf9qfB%= By}; vo`!4ChVv[12 !HGiyg):QVd8SmsQAj2~~AH3qU:?!4[a6SRu ǎ7H'1"_Qq!JbBwx$I-S^QO>AO~( HAPU)=Ojjm+ v$~ئ"33zviJn[*.\v(/E1U`Ycֿ&y3g>=x$;GS@]d1YÓo"۾X6n8o2 ,c_܊U?y" "cdL5HfFj~}Q]H錩/Oxcq'~lӕ_ ţeW\| &cLMhdȶ9-՗ $ Θڳ9i˗>xa6>#E _h2$}앿"a\l߰0/"ޑҦ.*:UQyٕ~`:oYfxu? b)<̜>җ'rYgԾ6ngeSMkm>uv" Snhj ̌ry_ݚLM01@$(]vƏ{_{#&>4l|c.8~rK05bjԈm;14*:Ο3yK|ީT\> 8nd٤B]j맻]8#&[5TEUlu#u\/kk^6t=Zo`Ӌ-,R'*EP1#EQ DfsnlOYYYҨ!${G2yZ~\pN|olӋnϯBu-\$5˘TYgNR^\8gF{@|4Ņ0ov2֊^:j)D"zM En1]WfN@wǛ뿨k B|c!>8T'JԉaZxubOW~;c%dLynظedNSt~WX\f-pO',9UI21`xĥd  ,{ER"Z G 4PLq@$#15! G}\.-2kEfV=G15Q&ph!9Ce Cvj(# 5#GX:InHJZmڞU__(h݆' H7cHκ})"Db-&`i\eU?*YJ05 D S[GabDěrqEʪ9կm"4LwtGTدr{OPۿhj?:}"i b:/7yA@eK#$t13mj51K &^w !%PSSSֆlr{s^#w4DmQI S#3a@57Q; S#:į v4yR+A&P0j/))-&Z4S.[Z2d^!j8J01-j(T!05Q)"jԌ+@vpd"'4LuyC͉cv,@A1i_qLq|s4bvGz!U !KIQD1E3[1vI $00h6FL̙dnu˞?SScw\LGaʃcf-N]y/4u: c c PM18_h>4~h޽f l%&N^>?2=iC)9v!˜j>hN'N~(aİ}Wx+' u0?1sL _/>_nH ! x9zq@bzlLؘO_6Ac6~t=F&מc2\汋rh3.婓Jx`x^_>_mqKkj+-++Y.zw3TU+qܹ~M\_:pBI" D5 JcTubd!P%+~fz*EP]6R2;/uz] g,'Nd=C^n188D,dZ}W/)~ǎ/z~*0P]g*ݐ[{s]b76 $?`[퍘JTDDKŽ t "((}qqwZΦO11fZ XSXk71E~;{GbN#"k" r@4˗mrN"srLڀ?Vh?݁nw'?0l۶`bF4]2UU ;llgL bkx'ۄ&%QU#c*B{awE|DǶBhZ-f/wIENDB`traits-4.6.0/docs/source/_static/et.ico000066400000000000000000000236261300633736300200260ustar00rootroot00000000000000(f 00hvh F00( r5(H73iKD]ZZzols$jV4{H5Fg@Hg @' 9c]sE{c@Ec{bFQEPkG\ 1}m( @_-!p3%7)&[B<]VU~okWU#XW$iU$zu5{j6Yv1GWw"X f$I qJ QJ[JډJc4jJ5{J 1FJ qGI Q#W6 Q$j#XA${#h1IQ$j1< s4{A a5S Q}A\1J7|xp``?`8(0`W,"4(%p3%v>1IGF~QFja_~wtdFwG܆ h즋" ij""!zi"""!g""""ۆ"""""F얋""""""Gzޥ""""""Hi """"" ih s"""""  c"""""! R""""!|2"""!|{2""!|2"1"!5 y@y`"!""" 2""""G s"""""H b"""" i R""""!yR""""!qh2""""0i2""""GR"!z"""""hr"""r"""" """""FB""""#93"""""GB"""""62"""""hp"""""%""""" ht"""""#|"""""!"""""k """""""""""Y s"""""""""8 b"""""""&R"""""%2"""#|2"#k"Y?????( f-t1!j/#i1$n3$j2&r4&s4&s5&u5&]0'`1'^2'u5'v5'v6'x6';+(X2(_2(r4(u6(w7(x7(z7(y8(z8({8(P0)s7)z8){8)|9):*c5,i9-A1.R4.E3/W7/u=1x?2A97>;:{F:F?>{I>mH?DBAMDB~ODJHHOMLhSOQPPPQQSRRUTTaW\YXx_[c[]]]^^^b_^___dddpghhhmhkjijjjrksmnnnrpopppsssuttwwwyyy~{}}}t@4LhO\b$8PraKx_ *?VNdcpI 0M}Wfm:1n{|wFvu2!5yziAUSyxj?]GRryxj>E-BYyzk7l`, %3Jg~Z+DaT)9hzC&6Ooe" [q. *>;'#QX( <H/s=^( @m,6#>%D&K'O(R)P) Y+ q0!2%"`."8'#g1#4($l2%p3%q3%q4%0'&a0&r4&t5&u5&v5&e2'p4't4't5'u5'u6'v6'w6'x6'/)(r6(v6(x7(Y3)z8)v8*l6+2--m:-v;-w>1543Q93v@3lA7vC8;:9wF:><;N?<VA=?>>@??zK@AAAoJAFFF~SHbNIKKKnQKLLLrTMONNPNNZPQQQSRRVVUdXVXWWaWaX[ZZwa\d_^e^i_```iabbbedceedhhhphqh}mipijjjlkkplrlwmoooqpopppxqwrsssyvuwwwyyy{{}}}~~~ŻjjybO =iٙWa"HuihZ!.SWZ% 9bݫXxaBmnczq2 !*NwܐVr-% 3]wt\#%XzF' Dz:  EIv- N}I`')uHYH޾Hn65S|HJ' 9dH<%"DpHs0!.SDe(! ;g9pQ("GuM8b> .Uo0@m~1% KL  *Nwג7%%?4%% 3XжP& +_l,$ =g^/#AT($ ! 1>  )[4 #Ck,1{R(%+_f|xp``?`8(0`<5"=$H&/"S)m-*" -# X+ s0!b."o1"+%#h0#+%$l2$u3$p3%q3%q4%r4%+&&8)&V.&g2&r4&t4&t5&u5&.('O/'t5'u5'v5'u6'v6'q6(v7(y7(o7)}9),+*V2*g4*x9*:.,x;,/..70.C2.z=/211u=1l=2z@3666X?9:::}E:><<PB>~I>AAALAlKCODEEERFIIIUIUJKKK`OKMMMZPRRR\TS^TUUU_WaWYYYs_Zt`[`^^__^g_a``bbaccceeeofhggiiillksknmmpppxptsruuuxvxxxyyy}{y}z{{{|}}}~}~¿ȏՎV)IzEl~zEzEtgzE{=?bzEM+$0LqzE9!$ 8YzE5$CdzEc/$0LtzEW$$ :ZzEG$!Ce|?>$$0Ovo*R}u7$:Zc5 ?ac&!CeD $*JmN$$0R\%$ 4Rxf,! .eF <]v=)6u7!$EeږM'#(Tc-#0Ot׽a+ !AQ$# 8Yvh=$$6jB$ 1!!%Ty7!!!"A`&$6jQ&"%T@$Ay7!$6j`&!%SQ'$A?????traits-4.6.0/docs/source/_templates/000077500000000000000000000000001300633736300174205ustar00rootroot00000000000000traits-4.6.0/docs/source/_templates/layout.html000066400000000000000000000003631300633736300216250ustar00rootroot00000000000000{# Filename: .templates/layout.html #} {% extends '!layout.html' %} {% block relbaritems %} {% if current_page_name != 'index' %}
  • {{ title }}
  • {% endif %} {% endblock %} traits-4.6.0/docs/source/_templates/search.html000066400000000000000000000017451300633736300215620ustar00rootroot00000000000000{% extends "!search.html" %} {% block body %}

    Search

    Enter your search words into the box below and click search. Note that the search function automatically searches for all of the words. Pages containing some but not all of them won't appear in the result list.

    {% if search_performed %}

    Search Results

    {% if not search_results %}

    Your search did not match any results.

    {% endif %} {% endif %}
    {% if search_results %}
      {% for href, caption, context in search_results %}
    • {{ caption }}
      {{ context|e }}
    • {% endfor %}
    {% endif %}
    {% endblock %} traits-4.6.0/docs/source/conf.py000066400000000000000000000226301300633736300165650ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Traits documentation build configuration file, created by # sphinx-quickstart on Tue Jul 22 10:52:03 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys import os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. sys.path.append(os.path.abspath('_extensions')) sys.path.append(os.path.abspath('../../')) def mock_modules(): """ Optionally Mock missing modules to allow autodoc based documentation. The ``traits.has_dynamics_view`` imports the traitsui module and thus traitsui is needed so that the ``autodoc`` extension can extract the docstrings from the has_dynamics_view module. This function optionally mocks the traitsui module so that the traits documentation can be built without the traitui optional dependency installed. .. note:: The mock library is needed in order to mock the traitsui package. """ MOCK_MODULES = [] MOCK_TYPES = [] # Check to see if we need to mock the traitsui package try: import traitsui except ImportError: # Modules that we need to mock MOCK_MODULES = [ 'traitsui', 'traitsui.api', 'traitsui.delegating_handler'] # Collect the types from traitsui that are based on HasTraits # We will need to mock them in a special way so that # TraitDocumenter can properly identify and document traits. from traits.api import HasTraits, HasPrivateTraits MOCK_TYPES.append( ('traitsui.delegating_handler', 'DelegatingHandler', (HasTraits,))) MOCK_TYPES.append( ('traitsui.view_element', 'ViewSubElement', (HasPrivateTraits,))) else: return try: from mock import MagicMock except ImportError: if len(MOCK_MODULES) != 0: print( 'NOTE: TraitsUI is not installed and mock is not available to ' 'mock the missing modules, some classes will not be documented') return # Create the custom types for the HasTraits based traitsui objects. TYPES = { mock_type: type(mock_type, bases, {'__module__': path}) for path, mock_type, bases in MOCK_TYPES} class DocMock(MagicMock): """ The special sphinx friendly mocking class to mock missing packages. Based on the suggestion from http://docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules """ @classmethod def __getattr__(self, name): if name in ('__file__', '__path__'): # sphinx does not like getting a Mock object in this case. return '/dev/null' else: # Return a mock or a custom type as requested. return TYPES.get(name, DocMock(mocked_name=name)) # MagicMock does not define __call__ we do just to make sure # that we cover all cases. def __call__(self, *args, **kwards): return DocMock() @property def __name__(self): # Make sure that if sphinx asks for the name of a Mocked class # it gets a nice strings to use (instead of "DocMock") return self.mocked_name # Add the mocked modules to sys sys.modules.update( (mod_name, DocMock(mocked_name=mod_name)) for mod_name in MOCK_MODULES) # Report on what was mocked. print 'mocking modules {0} and types {1}'.format( MOCK_MODULES, [mocked[1] for mocked in MOCK_TYPES]) mock_modules() # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'refactordoc', 'sphinx.ext.viewcode', 'sphinx.ext.autosummary', 'traits.util.trait_documenter'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'traits' copyright = '2008-2016, Enthought' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. d = {} execfile(os.path.join('..', '..', 'traits', '__init__.py'), d) version = release = d['__version__'] # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for the autodoc extension. autodoc_default_flags =['members'] autodoc_member_order = 'bysource' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "Traits 4 User Manual" # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. html_logo = "e-logo-rev.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = "et.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = False # If false, no index is generated. #html_use_index = False # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Traitsdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'Traits.tex', 'Traits 4 User Manual', 'Enthought, Inc.', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = "enthought_logo.jpg" latex_logo = "e-logo-rev.png" # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Options for Texinfo output # -------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'traits', 'Traits 4 User Manual', 'Enthought, Inc.', 'Traits', 'Explicitly typed attributes for Python.', 'Python'), ] traits-4.6.0/docs/source/e-logo-rev.png000066400000000000000000000075111300633736300177510ustar00rootroot00000000000000PNG  IHDRoi*sRGB pHYs  tIMELIDATx]ktT>z\$m ȥ!TD jV|_CWph PT\"B$$6aIf9qfB%= By}; vo`!4ChVv[12 !HGiyg):QVd8SmsQAj2~~AH3qU:?!4[a6SRu ǎ7H'1"_Qq!JbBwx$I-S^QO>AO~( HAPU)=Ojjm+ v$~ئ"33zviJn[*.\v(/E1U`Ycֿ&y3g>=x$;GS@]d1YÓo"۾X6n8o2 ,c_܊U?y" "cdL5HfFj~}Q]H錩/Oxcq'~lӕ_ ţeW\| &cLMhdȶ9-՗ $ Θڳ9i˗>xa6>#E _h2$}앿"a\l߰0/"ޑҦ.*:UQyٕ~`:oYfxu? b)<̜>җ'rYgԾ6ngeSMkm>uv" Snhj ̌ry_ݚLM01@$(]vƏ{_{#&>4l|c.8~rK05bjԈm;14*:Ο3yK|ީT\> 8nd٤B]j맻]8#&[5TEUlu#u\/kk^6t=Zo`Ӌ-,R'*EP1#EQ DfsnlOYYYҨ!${G2yZ~\pN|olӋnϯBu-\$5˘TYgNR^\8gF{@|4Ņ0ov2֊^:j)D"zM En1]WfN@wǛ뿨k B|c!>8T'JԉaZxubOW~;c%dLynظedNSt~WX\f-pO',9UI21`xĥd  ,{ER"Z G 4PLq@$#15! G}\.-2kEfV=G15Q&ph!9Ce Cvj(# 5#GX:InHJZmڞU__(h݆' H7cHκ})"Db-&`i\eU?*YJ05 D S[GabDěrqEʪ9կm"4LwtGTدr{OPۿhj?:}"i b:/7yA@eK#$t13mj51K &^w !%PSSSֆlr{s^#w4DmQI S#3a@57Q; S#:į v4yR+A&P0j/))-&Z4S.[Z2d^!j8J01-j(T!05Q)"jԌ+@vpd"'4LuyC͉cv,@A1i_qLq|s4bvGz!U !KIQD1E3[1vI $00h6FL̙dnu˞?SScw\LGaʃcf-N]y/4u: c c PM18_h>4~h޽f l%&N^>?2=iC)9v!˜j>hN'N~(aİ}Wx+' u0?1sL _/>_nH ! x9zq@bzlLؘO_6Ac6~t=F&מc2\汋rh3.婓Jx`x^_>_mqKkj+-++Y.zw3TU+qܹ~M\_:pBI" D5 JcTubd!P%+~fz*EP]6R2;/uz] g,'Nd=C^n188D,dZ}W/)~ǎ/z~*0P]g*ݐ[{s]b76 $?`[퍘JTDDKŽ t "((}qqwZΦO11fZ XSXk71E~;{GbN#"k" r@4˗mrN"srLڀ?Vh?݁nw'?0l۶`bF4]2UU ;llgL bkx'ۄ&%QU#c*B{awE|DǶBhZ-f/wIENDB`traits-4.6.0/docs/source/index.rst000066400000000000000000000003771300633736300171330ustar00rootroot00000000000000Traits Documentation ==================== User Reference -------------- .. toctree:: :maxdepth: 3 traits_user_manual/index Developer Reference ------------------- .. toctree:: :maxdepth: 3 traits_api_reference/index * :ref:`search` traits-4.6.0/docs/source/traits_api_reference/000077500000000000000000000000001300633736300214405ustar00rootroot00000000000000traits-4.6.0/docs/source/traits_api_reference/adapter.rst000066400000000000000000000003161300633736300236120ustar00rootroot00000000000000:mod:`adapter` Module ===================== .. automodule:: traits.adapter :no-members: Class ----- .. autoclass:: Adapter .. automethod:: __init__ Function -------- .. autofunction:: adapts traits-4.6.0/docs/source/traits_api_reference/category.rst000066400000000000000000000003111300633736300240020ustar00rootroot00000000000000:mod:`category` Module ====================== .. automodule:: traits.category :no-members: Classes ------- .. autoclass:: MetaCategory .. autoclass:: MetaCategoryObject .. autoclass:: Categorytraits-4.6.0/docs/source/traits_api_reference/has_dynamic_views.rst000066400000000000000000000003561300633736300256720ustar00rootroot00000000000000:mod:`has_dynamic_views` Module =============================== .. automodule:: traits.has_dynamic_views :no-members: Classes ------- .. autoclass:: DynamicViewSubElement .. autoclass:: DynamicView .. autoclass:: HasDynamicViews traits-4.6.0/docs/source/traits_api_reference/has_traits.rst000066400000000000000000000030111300633736300243260ustar00rootroot00000000000000:mod:`has_traits` Module ======================== .. automodule:: traits.has_traits :no-members: Classes ------- .. autoclass:: ViewElement .. autoclass:: MetaHasTraits .. autoclass:: MetaInterface .. automethod:: __init__ .. automethod:: __call__ .. autoclass:: MetaHasTraitsObject .. automethod:: __init__ .. autoclass:: HasTraits :exclude-members: wrappers .. attribute:: wrappers :annotation: = | {'same': TraitChangeNotifyWrapper, | 'extended': ExtendedTraitChangeNotifyWrapper, | 'new': NewTraitChangeNotifyWrapper, | 'fast_ui': FastUITraitChangeNotifyWrapper, | 'ui': FastUITraitChangeNotifyWrapper} Mapping from dispatch type to notification wrapper class type .. autoclass:: HasStrictTraits .. autoclass:: HasPrivateTraits .. autoclass:: SingletonHasTraits .. autoclass:: SingletonHasStrictTraits .. autoclass:: SingletonHasPrivateTraits .. autoclass:: Vetoable .. autoclass:: Interface .. autoclass:: ISerializable .. autoclass:: traits_super ABC classes ----------- .. note:: These classes are only available when the abc module is present. .. autoclass:: ABCMetaHasTraits .. autoclass:: ABCHasTraits .. autoclass:: ABCHasStrictTraits Functions --------- .. autofunction:: cached_property .. autofunction:: get_delegate_pattern .. autofunction:: implements .. autofunction:: on_trait_change .. autofunction:: property_depends_on .. autofunction:: provides .. autofunction:: weak_arg traits-4.6.0/docs/source/traits_api_reference/index.rst000066400000000000000000000010661300633736300233040ustar00rootroot00000000000000API Reference ============= Traits core ----------- .. toctree:: :maxdepth: 1 traits adapter category has_traits has_dynamic_views interface_checker trait_base trait_errors trait_handlers trait_numeric trait_types trait_value traits_listener trait_notifiers ustr_trait Subpackages ----------- .. toctree:: :maxdepth: 1 traits.adaptation traits.etsconfig traits.protocols traits.testing traits.util Indices and tables ================== * :ref:`genindex` * :ref:`search` traits-4.6.0/docs/source/traits_api_reference/interface_checker.rst000066400000000000000000000004021300633736300256120ustar00rootroot00000000000000:mod:`interface_checker` Module =============================== .. automodule:: traits.interface_checker :no-members: Classes ------- .. autoclass:: InterfaceError .. autoclass:: InterfaceChecker Function -------- .. autofunction:: check_implementstraits-4.6.0/docs/source/traits_api_reference/trait_base.rst000066400000000000000000000012551300633736300243120ustar00rootroot00000000000000:mod:`trait_base` Module ======================== .. automodule:: traits.trait_base :no-members: Classes ------- .. autodata:: Uninitialized .. autodata:: Undefined .. autodata:: Missing .. autodata:: Self Functions --------- .. autofunction:: strx .. autofunction:: class_of .. autofunction:: add_article .. autofunction:: user_name_for .. autofunction:: traits_home .. autofunction:: verify_path .. autofunction:: get_module_name .. autofunction:: get_resource_path .. autofunction:: xgetattr .. autofunction:: xsetattr .. autofunction:: is_none .. autofunction:: not_none .. autofunction:: not_false .. autofunction:: not_event .. autofunction:: is_str traits-4.6.0/docs/source/traits_api_reference/trait_errors.rst000066400000000000000000000004211300633736300247060ustar00rootroot00000000000000:mod:`trait_errors` Module ========================== .. automodule:: traits.trait_errors :no-members: Functions --------- .. autofunction:: repr_type Classes ------- .. autoclass:: TraitError .. autoclass:: TraitNotificationError .. autoclass:: DelegationError traits-4.6.0/docs/source/traits_api_reference/trait_handlers.rst000066400000000000000000000024531300633736300252010ustar00rootroot00000000000000:mod:`trait_handlers` Module ============================ .. automodule:: traits.trait_handlers :no-members: Classes ------- .. autoclass:: BaseTraitHandler .. autoclass:: NoDefaultSpecified .. autoclass:: TraitType .. autoclass:: TraitHandler .. autoclass:: TraitRange .. autoclass:: TraitString .. autoclass:: TraitCoerceType .. autoclass:: TraitCastType .. autoclass:: ThisClass .. autoclass:: TraitInstance .. autoclass:: TraitWeakRef .. autoclass:: HandleWeakRef .. autoclass:: TraitClass .. autoclass:: TraitFunction .. autoclass:: TraitEnum .. autoclass:: TraitPrefixList .. autoclass:: TraitMap .. autoclass:: TraitPrefixMap .. autoclass:: TraitExpression .. autoclass:: TraitCompound .. autoclass:: TraitTuple .. autoclass:: TraitCallable .. autoclass:: TraitListEvent .. autoclass:: TraitList .. autoclass:: TraitListObject .. autoclass:: TraitSetEvent .. autoclass:: TraitSetObject .. autoclass:: TraitDictEvent .. autoclass:: TraitDict .. autoclass:: TraitDictObject Private Functions ----------------- .. autofunction:: traits.trait_handlers._arg_count .. autofunction:: traits.trait_handlers._write_only .. autofunction:: traits.trait_handlers._read_only .. autofunction:: traits.trait_handlers._undefined_get .. autofunction:: traits.trait_handlers._undefined_set traits-4.6.0/docs/source/traits_api_reference/trait_notifiers.rst000066400000000000000000000012431300633736300253770ustar00rootroot00000000000000:mod:`trait_notifiers` Module ============================= .. automodule:: traits.trait_notifiers :no-members: Classes ------- .. autoclass:: NotificationExceptionHandlerState .. autoclass:: NotificationExceptionHandler :members: _push_handler, _pop_handler, _handle_exception, _get_handlers, _check_lock, _log_exception .. autoclass:: StaticAnyTraitChangeNotifyWrapper .. autoclass:: StaticTraitChangeNotifyWrapper .. autoclass:: TraitChangeNotifyWrapper .. autoclass:: ExtendedTraitChangeNotifyWrapper .. autoclass:: FastUITraitChangeNotifyWrapper .. autoclass:: NewTraitChangeNotifyWrapper Functions --------- .. autofunction:: set_ui_handler traits-4.6.0/docs/source/traits_api_reference/trait_numeric.rst000066400000000000000000000004321300633736300250360ustar00rootroot00000000000000:mod:`trait_numeric` Module =========================== .. automodule:: traits.trait_numeric :no-members: Classes ------- .. autoclass:: AbstractArray .. autoclass:: Array .. autoclass:: ArrayOrNone .. autoclass:: CArray Function -------- .. autofunction:: dtype2trait traits-4.6.0/docs/source/traits_api_reference/trait_types.rst000066400000000000000000000055321300633736300245460ustar00rootroot00000000000000:mod:`trait_types` Module ========================= .. automodule:: traits.trait_types :no-members: Traits ------ .. autoclass:: Any .. autoclass:: Generic .. autoclass:: BaseInt .. autoclass:: Int .. autoclass:: BaseLong .. autoclass:: Long .. autoclass:: BaseFloat .. autoclass:: Float .. autoclass:: BaseComplex .. autoclass:: Complex .. autoclass:: BaseStr .. autoclass:: Str .. autoclass:: Title .. autoclass:: BaseUnicode .. autoclass:: Unicode .. autoclass:: BaseBytes .. autoclass:: Bytes .. autoclass:: BaseBool .. autoclass:: Bool .. autoclass:: BaseCInt .. autoclass:: CInt .. autoclass:: BaseCLong .. autoclass:: CLong .. autoclass:: BaseCFloat .. autoclass:: CFloat .. autoclass:: BaseCComplex .. autoclass:: CComplex .. autoclass:: BaseCStr .. autoclass:: CStr .. autoclass:: BaseCUnicode .. autoclass:: CUnicode .. autoclass:: BaseCBytes .. autoclass:: CBytes .. autoclass:: BaseCBool .. autoclass:: CBool .. autoclass:: String .. autoclass:: Regex .. autoclass:: Code .. autoclass:: HTML .. autoclass:: Password .. autoclass:: Callable .. autoclass:: BaseType .. autoclass:: This .. autoclass:: self .. autoclass:: Function .. autoclass:: Method .. autoclass:: Class .. autoclass:: Module .. autoclass:: Python .. autoclass:: ReadOnly .. autoclass:: Disallow .. autoclass:: Constant .. autoclass:: Delegate .. autoclass:: DelegatesTo .. autoclass:: PrototypedFrom .. autoclass:: Expression .. autoclass:: PythonValue .. autoclass:: BaseFile .. autoclass:: File .. autoclass:: BaseDirectory .. autoclass:: Directory .. autoclass:: BaseRange .. autoclass:: Range .. autoclass:: BaseEnum .. autoclass:: Enum .. autoclass:: BaseTuple :special-members: __init__ .. autoclass:: Tuple .. autoclass:: ValidatedTuple :special-members: __init__ .. autoclass:: List .. autoclass:: CList .. autoclass:: Set .. autoclass:: CSet .. autoclass:: Dict .. autoclass:: BaseClass .. autoclass:: BaseInstance .. autoclass:: Instance .. autoclass:: Supports .. autoclass:: AdaptsTo .. autoclass:: Type .. autoclass:: Event .. autoclass:: Button .. autoclass:: ToolbarButton .. autoclass:: Either .. autoclass:: Symbol .. autoclass:: UUID .. autoclass:: WeakRef :members: __init__ .. autodata:: Date .. autodata:: Time .. autodata:: ListInt .. autodata:: ListFloat .. autodata:: ListStr .. autodata:: ListUnicode .. autodata:: ListComplex .. autodata:: ListBool .. autodata:: ListFunction .. autodata:: ListMethod .. autodata:: ListClass .. autodata:: ListInstance .. autodata:: ListThis .. autodata:: DictStrAny .. autodata:: DictStrStr .. autodata:: DictStrInt .. autodata:: DictStrLong .. autodata:: DictStrFloat .. autodata:: DictStrBool .. autodata:: DictStrList Private Classes --------------- .. autoclass:: HandleWeakRef Functions --------- .. autofunction:: default_text_editor traits-4.6.0/docs/source/traits_api_reference/trait_value.rst000066400000000000000000000004421300633736300245110ustar00rootroot00000000000000:mod:`trait_value` Module ========================= .. automodule:: traits.trait_value :no-members: Classes ------- .. autoclass:: BaseTraitValue .. autoclass:: TraitValue Functions --------- .. autofunction:: SyncValue .. autofunction:: TypeValue .. autofunction:: DefaultValuetraits-4.6.0/docs/source/traits_api_reference/traits.adaptation.rst000066400000000000000000000020401300633736300256170ustar00rootroot00000000000000:mod:`adaptation` Package ========================= :mod:`adaptation` Package ------------------------- .. automodule:: traits.adaptation :members: :undoc-members: :show-inheritance: :mod:`adaptation_error` Module ------------------------------ .. automodule:: traits.adaptation.adaptation_error :members: :undoc-members: :show-inheritance: :mod:`adaptation_manager` Module -------------------------------- .. automodule:: traits.adaptation.adaptation_manager :members: :undoc-members: :show-inheritance: :mod:`adaptation_offer` Module ------------------------------ .. automodule:: traits.adaptation.adaptation_offer :members: :undoc-members: :show-inheritance: :mod:`adapter` Module --------------------- .. automodule:: traits.adaptation.adapter :members: :undoc-members: :show-inheritance: :mod:`cached_adapter_factory` Module ------------------------------------ .. automodule:: traits.adaptation.cached_adapter_factory :members: :undoc-members: :show-inheritance: traits-4.6.0/docs/source/traits_api_reference/traits.etsconfig.rst000066400000000000000000000004371300633736300254640ustar00rootroot00000000000000:mod:`etsconfig` Package ======================== :mod:`etsconfig` Package ------------------------ .. automodule:: traits.etsconfig :no-members: :mod:`etsconfig` Module ----------------------- .. automodule:: traits.etsconfig.etsconfig :no-members: .. autodata:: ETSConfig traits-4.6.0/docs/source/traits_api_reference/traits.protocols.rst000066400000000000000000000004131300633736300255210ustar00rootroot00000000000000:mod:`protocols` Package ======================== .. note:: The :mod:`traits.protocols` package is deprecated. Use the :mod:`traits.adaptation` package instead in new code. .. automodule:: traits.protocols :members: :undoc-members: :show-inheritance: traits-4.6.0/docs/source/traits_api_reference/traits.rst000066400000000000000000000015461300633736300235060ustar00rootroot00000000000000:mod:`traits` Module ==================== .. automodule:: traits.traits :no-members: .. currentmodule:: traits.traits Classes ------- .. autoclass:: CTrait .. autoclass:: TraitFactory .. autoclass:: TraitImportError .. autoclass:: Default .. autoclass:: Property .. autoclass:: ForwardProperty .. autoclass:: Color .. autoclass:: RGBColor .. autoclass:: Font Functions --------- .. autofunction:: Trait .. autofunction:: password_editor .. autofunction:: multi_line_text_editor .. autofunction:: code_editor .. autofunction:: shell_editor .. autofunction:: time_editor .. autofunction:: date_editor .. autofunction:: trait_factory .. autofunction:: trait_cast .. autofunction:: try_trait_cast .. autofunction:: trait_from Private Classes --------------- .. autoclass:: traits.traits._InstanceArgs .. autoclass:: traits.traits._TraitMaker traits-4.6.0/docs/source/traits_api_reference/traits.testing.rst000066400000000000000000000012301300633736300251500ustar00rootroot00000000000000:mod:`testing` Package ====================== :mod:`testing` Package ---------------------- .. automodule:: traits.testing :members: :undoc-members: :show-inheritance: :mod:`doctest_tools` Module --------------------------- .. automodule:: traits.testing.doctest_tools :members: :undoc-members: :show-inheritance: :mod:`nose_tools` Module ------------------------ .. automodule:: traits.testing.nose_tools :members: :undoc-members: :show-inheritance: :mod:`trait_assert_tools` Module -------------------------------- .. automodule:: traits.testing.unittest_tools :members: :undoc-members: :show-inheritance: traits-4.6.0/docs/source/traits_api_reference/traits.util.rst000066400000000000000000000032321300633736300244540ustar00rootroot00000000000000:mod:`util` Package =================== :mod:`util` Package ------------------- .. automodule:: traits.util :members: :undoc-members: :show-inheritance: :mod:`async_trait_wait` Module ------------------------------ .. automodule:: traits.util.async_trait_wait :members: :undoc-members: :show-inheritance: :mod:`camel_case` Module ------------------------ .. automodule:: traits.util.camel_case :members: :undoc-members: :show-inheritance: :mod:`clean_strings` Module --------------------------- .. automodule:: traits.util.clean_strings :members: :undoc-members: :show-inheritance: :mod:`deprecated` Module ------------------------ .. automodule:: traits.util.deprecated :members: :undoc-members: :show-inheritance: :mod:`home_directory` Module ---------------------------- .. automodule:: traits.util.home_directory :members: :undoc-members: :show-inheritance: :mod:`resource` Module ---------------------- .. automodule:: traits.util.resource :members: :undoc-members: :show-inheritance: :mod:`import_symbol` Module ----------------------------- .. automodule:: traits.util.import_symbol :members: :undoc-members: :show-inheritance: :mod:`toposort` Module ---------------------- .. automodule:: traits.util.toposort :members: :undoc-members: :show-inheritance: :mod:`trait_documenter` Module ------------------------------ .. automodule:: traits.util.trait_documenter :members: :undoc-members: :show-inheritance: :mod:`event_tracer` Module ------------------------------ .. automodule:: traits.util.event_tracer :members: :undoc-members: traits-4.6.0/docs/source/traits_api_reference/traits_listener.rst000066400000000000000000000002361300633736300254060ustar00rootroot00000000000000:mod:`traits_listener` Module ============================= .. automodule:: traits.traits_listener :members: :undoc-members: :show-inheritance: traits-4.6.0/docs/source/traits_api_reference/ustr_trait.rst000066400000000000000000000002171300633736300243720ustar00rootroot00000000000000:mod:`ustr_trait` Module ======================== .. automodule:: traits.ustr_trait :members: :undoc-members: :show-inheritance: traits-4.6.0/docs/source/traits_user_manual/000077500000000000000000000000001300633736300211645ustar00rootroot00000000000000traits-4.6.0/docs/source/traits_user_manual/advanced.rst000066400000000000000000001564151300633736300234770ustar00rootroot00000000000000.. _advanced-topics: =============== Advanced Topics =============== The preceding sections provide enough information for you to use traits for manifestly-typed attributes, with initialization and validation. This section describes the advanced features of the Traits package .. _initialization-and-validation-revisited: Initialization and Validation Revisited --------------------------------------- The following sections present advanced topics related to the initialization and validation features of the Traits package. * Dynamic initialization * Overriding default values * Reusing trait definitions * Trait attribute definition strategies .. index:: initialization; dynamic .. _dynamic-initialization: Dynamic Initialization `````````````````````` When you define trait attributes using predefined traits, the Trait() factory function or trait handlers, you typically specify their default values statically. You can also define a method that dynamically initializes a trait attribute the first time that the attribute value is accessed. To do this, you define a method on the same class as the trait attribute, with a name based on the name of the trait attribute: .. index:: default value; method .. method:: _name_default() This method initializes the *name* trait attribute, returning its initial value. The method overrides any default value specified in the trait definition. .. index:: get_default_value() It is also possible to define a dynamic method for the default value in a trait type subclass (get_default_value()). However, using a _\ *name*\ _default() method avoids the overhead of subclassing a trait. .. index:: default value; overriding in a subclass .. index:: pair: examples; overriding default values .. _overriding-default-values-in-a-subclass: Overriding Default Values in a Subclass ``````````````````````````````````````` Often, a subclass must override a trait attribute in a parent class by providing a different default value. You can specify a new default value without completely re-specifying the trait definition for the attribute. For example:: # override_default.py -- Example of overriding a default value for # a trait attribute in a subclass from traits.api import HasTraits, Range, Str class Employee(HasTraits): name = Str salary_grade = Range(value=1, low=1, high=10) class Manager(Employee): salary_grade = 5 In this example, the **salary_grade** of the Employee class is a range from 1 to 10, with a default value of 1. In the Manager subclass, the default value of **salary_grade** is 5, but it is still a range as defined in the Employee class. .. index:: trait; definitions; reusing .. _reusing-trait-definitions: Reusing Trait Definitions ````````````````````````` As mentioned in :ref:`defining-traits-initialization-and-validation`, in most cases, traits are defined in-line in attribute definitions, but they can also be defined independently. A trait definition only describes the characteristics of a trait, and not the current value of a trait attribute, so it can be used in the definition of any number of attributes. For example:: # trait_reuse.py --- Example of reusing trait definitions from traits.api import HasTraits, Range coefficient = Range(-1.0, 1.0, 0.0)) class quadratic(HasTraits): c2 = coefficient c1 = coefficient c0 = coefficient x = Range(-100.0, 100.0, 0.0) In this example, a trait named **coefficient** is defined externally to the class **quadratic**, which references **coefficient** in the definitions of its trait attributes **c2**, **c1**, and **c0**. Each of these attributes has a unique value, but they all use the same trait definition to determine whether a value assigned to them is valid. .. index:: explicit trait attribute definition .. _trait-attribute-definition-strategies: Trait Attribute Definition Strategies ````````````````````````````````````` In the preceding examples in this guide, all trait attribute definitions have bound a single object attribute to a specified trait definition. This is known as "explicit" trait attribute definition. The Traits package supports other strategies for defining trait attributes. You can associate a category of attributes with a particular trait definition, using the trait attribute name wildcard. You can also dynamically create trait attributes that are specific to an instance, using the add_trait() method, rather than defined on a class. These strategies are described in the following sections. .. index:: wildcard; trait attribute names pair: wildcard; examples .. _trait-attribute-name-wildcard: Trait Attribute Name Wildcard ::::::::::::::::::::::::::::: The Traits package enables you to define a category of trait attributes associated with a particular trait definition, by including an underscore ('_') as a wildcard at the end of a trait attribute name. For example:: # temp_wildcard.py --- Example of using a wildcard with a Trait # attribute name from traits.api import Any, HasTraits class Person(HasTraits): temp_ = Any This example defines a class Person, with a category of attributes that have names beginning with ``temp``, and that are defined by the Any trait. Thus, any part of the program that uses a Person instance can reference attributes such as **tempCount**, **temp_name**, or **temp_whatever**, without having to explicitly declare these trait attributes. Each such attribute has None as the initial value and allows assignment of any value (because it is based on the Any trait). You can even give all object attributes a default trait definition, by specifying only the wildcard character for the attribute name:: # all_wildcard.py --- Example of trait attribute wildcard rules from traits.api import Any, HasTraits, Int, Str class Person ( HasTraits ): # Normal, explicitly defined trait: name = Str # By default, let all traits have any value: _ = Any # Except for this one, which must be an Int: age = Int """ >>> bill = Person() >>> # These assignments should all work: >>> bill.name = 'William' >>> bill.address = '121 Drury Lane' >>> bill.zip_code = 55212 >>> bill.age = 49 >>> # This should generate an error (must be an Int): >>> bill.age = 'middle age' Traceback (most recent call last): File "all_wildcard.py", line 33, in bill.age = 'middle age' File "c:\wrk\src\lib\enthought\traits\\trait_handlers.py", line 163, in error raise TraitError, ( object, name, self.info(), value ) TraitError: The 'age' trait of a Person instance must be an integer, but a value of 'middle age' was specified. """ In this case, all Person instance attributes can be created on the fly and are defined by the Any trait. .. index:: wildard; rules .. _wildcard-rules: Wildcard Rules '''''''''''''' When using wildcard characters in trait attribute names, the following rules are used to determine what trait definition governs an attribute: 1. If an attribute name exactly matches a name without a wildcard character, that definition applies. 2. Otherwise, if an attribute name matches one or more names with wildcard characters, the definition with the longest name applies. Note that all possible attribute names are covered by one of these two rules. The base HasTraits class implicitly contains the attribute definition ``_ = Python``. This rule guarantees that, by default, all attributes have standard Python language semantics. These rules are demonstrated by the following example:: # wildcard_rules.py -- Example of trait attribute wildcard rules from traits.api import Any, HasTraits, Int, Python class Person(HasTraits): temp_count = Int(-1) temp_ = Any _ = Python In this example, the Person class has a **temp_count** attribute, which must be an integer and which has an initial value of -1. Any other attribute with a name starting with ``temp`` has an initial value of None and allows any value to be assigned. All other object attributes behave like normal Python attributes (i.e., they allow any value to be assigned, but they must have a value assigned to them before their first reference). .. index:: Disallow; object, examples; Disallow object .. _disallow-object: Disallow Object ''''''''''''''' The singleton object Disallow can be used with wildcards to disallow all attributes that are not explicitly defined. For example:: # disallow.py --- Example of using Disallow with wildcards from traits.api import \ Disallow, Float, HasTraits, Int, Str class Person (HasTraits): name = Str age = Int weight = Float _ = Disallow In this example, a Person instance has three trait attributes: * **name**: Must be a string; its initial value is ''. * **age**: Must be an integer; its initial value is 0. * **weight**: Must be a float; its initial value is 0.0. All other object attributes are explicitly disallowed. That is, any attempt to read or set any object attribute other than **name**, **age**, or **weight** causes an exception. .. index:: HasTraits class; predefined subclasses .. _hastraits-subclasses: HasTraits Subclasses '''''''''''''''''''' Because the HasTraits class implicitly contains the attribute definition ``_ = Python``, subclasses of HasTraits by default have very standard Python attribute behavior for any attribute not explicitly defined as a trait attribute. However, the wildcard trait attribute definition rules make it easy to create subclasses of HasTraits with very non-standard attribute behavior. Two such subclasses are predefined in the Traits package: HasStrictTraits and HasPrivateTraits. .. index:: HasStrictTraits class .. _hasstricttraits: HasStrictTraits ''''''''''''''' This class guarantees that accessing any object attribute that does not have an explicit or wildcard trait definition results in an exception. This can be useful in cases where a more rigorous software engineering approach is employed than is typical for Python programs. It also helps prevent typos and spelling mistakes in attribute names from going unnoticed; a misspelled attribute name typically causes an exception. The definition of HasStrictTraits is the following:: class HasStrictTraits(HasTraits): _ = Disallow HasStrictTraits can be used to create type-checked data structures, as in the following example:: class TreeNode(HasStrictTraits): left = This right = This value = Str This example defines a TreeNode class that has three attributes: **left**, **right**, and **value**. The **left** and **right** attributes can only be references to other instances of TreeNode (or subclasses), while the **value** attribute must be a string. Attempting to set other types of values generates an exception, as does attempting to set an attribute that is not one of the three defined attributes. In essence, TreeNode behaves like a type-checked data structure. .. index:: HasPrivateTraits class .. _hasprivatetraits: HasPrivateTraits '''''''''''''''' This class is similar to HasStrictTraits, but allows attributes beginning with '_' to have an initial value of None, and to not be type-checked. This is useful in cases where a class needs private attributes, which are not part of the class's public API, to keep track of internal object state. Such attributes do not need to be type-checked because they are only manipulated by the (presumably correct) methods of the class itself. The definition of HasPrivateTraits is the following:: class HasPrivateTraits(HasTraits): __ = Any _ = Disallow These subclasses of HasTraits are provided as a convenience, and their use is completely optional. However, they do illustrate how easy it is to create subclasses with customized default attribute behavior if desired. .. index:: trait attributes; per-object .. _per-object-trait-attributes: Per-Object Trait Attributes ''''''''''''''''''''''''''' The Traits package allows you to define dynamic trait attributes that are object-, rather than class-, specific. This is accomplished using the add_trait() method of the HasTraits class: .. method:: add_trait(name, trait) .. index:: examples; per-object trait attributes For example:: # object_trait_attrs.py --- Example of per-object trait attributes from traits.api import HasTraits, Range class GUISlider (HasTraits): def __init__(self, eval=None, label='Value', trait=None, min=0.0, max=1.0, initial=None, **traits): HasTraits.__init__(self, **traits) if trait is None: if min > max: min, max = max, min if initial is None: initial = min elif not (min <= initial <= max): initial = [min, max][ abs(initial - min) > abs(initial - max)] trait = Range(min, max, value = initial) self.add_trait(label, trait) This example creates a GUISlider class, whose __init__() method can accept a string label and either a trait definition or minimum, maximum, and initial values. If no trait definition is specified, one is constructed based on the **max** and **min** values. A trait attribute whose name is the value of label is added to the object, using the trait definition (whether specified or constructed). Thus, the label trait attribute on the GUISlider object is determined by the calling code, and added in the __init__() method using add_trait(). You can require that add_trait() must be used in order to add attributes to a class, by deriving the class from HasStrictTraits (see :ref:`hasstricttraits`). When a class inherits from HasStrictTraits, the program cannot create a new attribute (either a trait attribute or a regular attribute) simply by assigning to it, as is normally the case in Python. In this case, add_trait() is the only way to create a new attribute for the class outside of the class definition. .. index:: interfaces .. _interfaces: Interfaces ---------- The Traits package supports declaring and implementing *interfaces*. An interface is an abstract data type that defines a set of attributes and methods that an object must have to work in a given situation. The interface says nothing about what the attributes or methods do, or how they do it; it just says that they have to be there. Interfaces in Traits are similar to those in Java. They can be used to declare a relationship among classes which have similar behavior but do not have an inheritance relationship. Like Traits in general, Traits interfaces don't make anything possible that is not already possible in Python, but they can make relationships more explicit and enforced. Python programmers routinely use implicit, informal interfaces (what's known as "duck typing"). Traits allows programmers to define explicit and formal interfaces, so that programmers reading the code can more easily understand what kinds of objects are actually *intended* to be used in a given situation. .. index:: interfaces; defining, examples; interface definition .. _defining-an-interface: Defining an Interface ````````````````````` To define an interface, create a subclass of Interface:: from traits.api import Interface class IName(Interface): def get_name(self): """ Returns a string which is the name of an object. """ Interface classes serve primarily as documentation of the methods and attributes that the interface defines. In this case, a class that implements the ``IName`` interface must have a method named ``get_name()``, which takes no arguments and returns a string. Do not include any implementation code in an interface declaration. However, the Traits package does not actually check to ensure that interfaces do not contain implementations. By convention, interface names have a capital 'I' at the beginning of the name. .. index:: interfaces; implementing .. _implementing-an-interface: Implementing an Interface ````````````````````````` A class declares that it implements one or more interfaces using the :func:`~traits.api.provides` class decorator, which has the signature: .. currentmodule:: traits.has_traits .. function:: provides(interface[, interface2 , ... , interfaceN]) :noindex: .. index:: examples; interface implementation, interfaces; implementation; example Interface names beyond the first one are optional. As for all class decorators, the call to provides must occur just before the class definition. For example:: from traits.api import HasTraits, Interface, provides, Str class IName(Interface): def get_name(self): """ Returns a string which is the name of an object. """ @provides(IName) class Person(HasTraits): first_name = Str( 'John' ) last_name = Str( 'Doe' ) # Implementation of the 'IName' interface: def get_name ( self ): ''' Returns the name of an object. ''' name = '{first} {last}' return name.format(name=self.first_name, last=self.last_name) You can specify whether the provides() decorator verifies that the class calling it actually implements the interface that it says it does. This is determined by the CHECK_INTERFACES variable, which can take one of three values: * 0 (default): Does not check whether classes implement their declared interfaces. * 1: Verifies that classes implement the interfaces they say they do, and logs a warning if they don't. * 2: Verifies that classes implement the interfaces they say they do, and raises an InterfaceError if they don't. The CHECK_INTERFACES variable must be imported directly from the traits.has_traits module:: import traits.has_traits traits.has_traits.CHECK_INTERFACES = 1 .. index:: interfaces; using, examples; interface usage .. _using-interfaces: Using Interfaces ```````````````` You can use an interface at any place where you would normally use a class name. The most common way to use interfaces is with the :class:`~traits.trait_types.Instance` or :class:`~traits.trait_types.Supports` traits:: >>> from traits.api import HasTraits, Instance >>> class Apartment(HasTraits): ... renter = Instance(IName) >>> william = Person(first_name='William', last_name='Adams') >>> apt1 = Apartment( renter=william ) >>> print 'Renter is: ', apt1.renter.get_name() Renter is: William Adams Using an interface class with an ``Instance`` trait definition declares that the trait accepts only values that implement the specified interface. Using the ``Supports`` traits, if the assigned object does not implement the interface, the Traits package may automatically substitute an adapter object that implements the specified interface. See :ref:`adaptation` for more information. .. index:: adaptation .. _adaptation: Adaptation ---------- *The adaptation features of Traits have been rewritten in v. 4.4.0 . See the* :ref:`migration guide ` *below for details regarding changes in API.* Adaptation is the process of transforming an object that does not implement a specific interface needed by a client into an object that does. In the adapter pattern, an object is wrapped in a second object, the *adapter*, that implements the target interface. Adaptation enables a programming style in which each component or service in an application defines an interface through which it would like to receive information. Objects that need to communicate with the component declare an adapter for that interface, as illustrated in the figure below. .. image:: images/adaptation.png Adaptation allows decoupling the data model from the application components and services: introducing a new component in the application should not require modifying the data objects! Traits provides a package to make this pattern easy and automatic: In the :mod:`traits.adaptation` package, adapters from a protocol (type or interface) to another can be registered with a manager object. HasTraits classes can either explicitly request to adapt an object to a protocol, or they can define special traits that automatically invoke the adaptation manager whenever it is necessary. For example, if a :class:`~traits.trait_types.Supports` trait requires its values to implement interface ``IPrintable``, and an object is assigned to it which is of class ``Image``, which does not implement ``IPrintable``, then Traits looks for an adapter from ``Image`` to ``IPrintable``, and if one exists the adapter object is assigned to the trait. If necessary, a "chain" of adapter objects might be created, in order to perform the required adaptation. Main features ````````````` The main features of the :mod:`traits.adaptation` package are: * Support for Python classes, ABCs, and traits :class:`~.Interface` s Protocols can be specified using any of those. * Chaining of adapters Adapters can be chained, i.e., an object can be adapted to a target protocol as long as there is a sequence of adapters that can be used to transform it. * Conditional adaptation Adaptation of an object to a protocol can be conditional, i.e. it may succeed or fail depending on the state of the object. * Lazy loading The classes for the adapter, the origin, and the target protocols can be specified as strings, and are only loaded if they are required. Note on terminology ``````````````````` To avoid confusion, let's define two terms that we will use all the time: * We say that a class *provides* a protocol if it is a subclass of the protocol, or if it implements the protocol (if it is an interface) * We say that a class *supports* a protocol if it provides the protocol or an adapter object can be built that provides the protocol .. index:: adapters; defining .. _defining-adapters: Defining Adapters ````````````````` .. index:: Adapter class .. _subclassing-adapter: The :class:`Adapter` class :::::::::::::::::::::::::: The Traits package provides two classes for defining adapters, one for Traits adapters, :class:`~traits.adaptation.adapter.Adapter`, and one for for pure-Python adapters, :class:`~traits.adaptation.adapter.PurePythonAdapter`. These classes streamline the process of creating a new adapter class. They have a standard constructor that does not normally need to be overridden by subclasses. This constructor accepts one parameter, which is the object to be adapted, and assigns that object to an :attr:`adaptee` attribute (a trait in the case of :class:`~traits.adaptation.adapter.Adapter`). As an adapter writer, you need to take care of the following: * Declare which interfaces the adapter class implements on behalf of the object it is adapting. For example, if we are working with Traits :class:`~.Interface` s, the adapter would be decorated with the :func:`~traits.has_traits.provides` decorator. In the case of Python ABCs, the class would be a subclass of the abstract base class, or be `registered with it `_. * Implement the methods defined in the interfaces declared in the previous step. Usually, these methods are implemented using appropriate members on the adaptee object. .. index:: adaptee trait * For Traits adapters, define a trait attribute named **adaptee** that declares what type of object it is an adapter for. Usually, this is an :class:`~.Instance` trait. .. index:: pair: example; Adapter class The following code example shows a definition of a simple adapter class:: from traits.api import Adapter, Instance, provides # Declare what interfaces this adapter implements for its client @provides(IName) class PersonToIName(Adapter): # Declare the type of client it supports: adaptee = Instance(Person) # Implement the 'IName' interface on behalf of its client: def get_name ( self ): name = '{first} {last}'.format(first=self.adaptee.first_name, last=self.adaptee.last_name) return name .. index:: pair: registering; adapters .. _registering-adapters: Registering adapters :::::::::::::::::::: Once an adapter class has been defined, it has to be registered with the adaptation manager using the :func:`~traits.adaptation.api.register_factory` function. The signature of :func:`~traits.adaptation.api.register_factory` is: .. currentmodule:: traits.adaptation.api .. function:: register_factory(adapter_class, from_protocol, to_protocol) :noindex: The :func:`~traits.adaptation.adaptation_manager.register_factory` function takes as first argument the adapter class (or an :ref:`adapter factory `), followed by the protocol to be adapted (the one provided by the adaptee, :attr:`from_protocol`), and the protocol that it provides (:attr:`to_protocol`). .. index:: examples; registering adapters This is the example from the previous section, were the adapter is registered:: from traits.adaptation.api import Adapter, Instance, provides # Declare what interfaces this adapter implements for its client @provides(IName) class PersonToIName(Adapter): # Declare the type of client it supports: adaptee = Instance(Person) # Implement the 'IName' interface on behalf of its client: def get_name ( self ): name = '{first} {last}'.format(first=self.adaptee.first_name, last=self.adaptee.last_name) return name # ... somewhere else at application startup. register_factory(PersonToIName, Person, IName) .. index:: adapters; factory; factories; conditional adaptation .. _adapter-factories: Adapter factories, and conditional adaptation ````````````````````````````````````````````` The first argument to the :func:`~traits.adaptation.api.register_factory` function needs not be an adapter *class*, it can be, more generally, an adapter *factory*. An adapter factory can be any callable that accepts one positional argument, the adaptee object, and returns an adapter or None if the adaptation was not possible. Adapter factories allow flexibility in the adaptation process, as the result of adaptation may vary depending on the state of the adaptee object. .. _conditional-adaptation: Conditional adaptation :::::::::::::::::::::: A common use of adapter factories is to allow adaptation only if the state of the adaptee object allows it. The factory returns an adapter object if adaptation is possible, or None if it is not. In the following example, a ``numpy.ndarray`` object can be adapted to provide an ``IImage`` protocol only if the number of dimensions is 2. (For illustration, this example uses Python ABCs rather than Traits Interfaces.) :: import abc import numpy from traits.api import Array, HasTraits from traits.adaptation.api import adapt, Adapter, register_factory class ImageABC(object): __metaclass__ = abc.ABCMeta class NDArrayToImage(Adapter): adaptee = Array # Declare that NDArrayToImage implements ImageABC. ImageABC.register(NDArrayToImage) def ndarray_to_image_abc(adaptee): """ An adapter factory from numpy arrays to the ImageABC protocol.""" if adaptee.ndim == 2: return NDArrayToImage(adaptee=adaptee) return None # ... somewhere else at application startup register_factory(ndarray_to_image_abc, numpy.ndarray, ImageABC) # Try to adapt numpy arrays to images. The `adapt` function is # introduced later in the docs, but you can probably guess what it does ;-) # This adaptation fails, as the array is 1D image = adapt(numpy.ndarray([1,2,3]), ImageABC, default=None) assert image == None # This succeeds. image = adapt(numpy.array([[1,2],[3,4]]), ImageABC) assert isinstance(image, NDArrayToImage) .. index:: pair: adapters; requesting .. _using-adapters: Requesting an adapter ````````````````````` The ``adapt`` function :::::::::::::::::::::: Adapter classes are defined as described in the preceding sections, but you do not explicitly create instances of these classes. Instead, the function :func:`~traits.adaptation.adaptation_manager.adapt` is used, giving the object that needs to be adapted and the target protocol. For instance, in the example in the :ref:`conditional-adaptation` section, a 2D numpy array is adapted to an ImageABC protocol with :: image = adapt(numpy.array([[1,2],[3,4]]), ImageABC) In some cases, no single adapter class is registered that adapts the object to the required interface, but a series of adapter classes exist that, together, perform the required adaptation. In such cases, the necessary set of adapter objects are created, and the "last" link in the chain, the one that actually implements the required interface, is returned. When a situation like this arises, the adapted object assigned to the trait always contains the smallest set of adapter objects needed to adapt the original object. Also, more specific adapters are preferred over less specific ones. For example, let's suppose we have a class ``Document`` and a subclass ``HTMLDocument``. We register two adapters to an interface ``IPrintable``, ``DocumentToIPrintable`` and ``HTMLDocumentToIPrintable``. The call :: html_doc = HTMLDocument() printable = adapt(html_doc, IPrintable) will return an instance of the ``HTMLDocumentToIPrintable`` adapter, as it is more specific than ``DocumentToIPrintable``. If no single adapter and no adapter chain can be constructed for the requested adaptation, an :class:`~traits.adaptation.adaptation_error.AdaptationError` is raised. Alternatively, one can specify a default value to be returned in this case:: printable = adapt(unprintable_doc, IPrintable, default=EmptyPrintableDoc()) Using Traits interfaces ::::::::::::::::::::::: An alternative syntax to create adapters when using Traits Interfaces is to use the interface class as an adapter factory, for example :: printable = IPrintable(html_doc, None) is equivalent to :: printable = adapt(html_doc, IPrintable, default=None) (the default argument, None, is optional). Using the ``Supports`` and ``AdaptsTo`` traits :::::::::::::::::::::::::::::::::::::::::::::: Using the terminology introduced in this section, we can say that the :class:`~.Instance` trait accepts values that *provide* the specified protocol. Traits defines two additional traits that accept values that *support* a given protocol (they provide it or can be adapted to it) instead: * The :class:`~.Supports` trait accepts values that support the specified protocol. The value of the trait after assignment is the possibly adapted value (i.e., it is the original assigned value if that provides the protocol, or is an adapter otherwise). * The :class:`~.AdaptsTo` trait also accepts values that support the specified protocol. Unlike ``Supports``, ``AdaptsTo`` stores the original, unadapted value. If your application works with adaptation, it is natural to use the ``Supports`` trait in place of the ``Instance`` one in most cases. This will allow that application to be extended by adaptation in the future without changing the existing code, without having to invoke adaptation explicitly in your code. For example, a Traits object can be written against the ``IPrintable`` interface and be open to extensions by adaptation as follows: :: from traits.api import (Adapter, HasTraits, Interface, List, provides, register_factory, Str, Supports) class IPrintable(Interface): def get_formatted_text(self, n_cols): """ Return text formatted with the given number of columns. """ class PrintQueue(HasTraits): # This is the key part of the example: we declare a list of # items that provide or can be adapted to IPrintable queue = List(Supports(IPrintable)) def is_empty(self): return len(self.queue) == 0 def push(self, printable): self.queue.append(printable) def print_next(self): printable = self.queue.pop(0) # The elements from the list are guaranteed to provide # IPrintable, so we can call the interface without worrying # about adaptation. lines = printable.get_formatted_text(n_cols=20) print '-- Start document --' print '\n'.join(lines) print '-- End of document -\n' class TextDocument(HasTraits): """ A text document. """ text = Str @provides(IPrintable) class TextDocumentToIPrintable(Adapter): """ Adapt TextDocument and provide IPrintable. """ def get_formatted_text(self, n_cols): import textwrap return textwrap.wrap(self.adaptee.text, n_cols) # ---- Application starts here. # Register the adapter. register_factory(TextDocumentToIPrintable, TextDocument, IPrintable) # Create two text documents. doc1 = TextDocument(text='very very long text the will bore you for sure') doc2 = TextDocument(text='once upon a time in a far away galaxy') # The text documents can be pushed on the print queue; in the process, # they are automatically adapted by Traits. print_queue = PrintQueue() print_queue.push(doc1) print_queue.push(doc2) while not print_queue.is_empty(): print_queue.print_next() This scripts produces this output: :: -- Start document -- very very long text the will bore you for sure -- End of document - -- Start document -- once upon a time in a far away galaxy -- End of document - Implementation details `````````````````````` The algorithm for finding a sequence of adapters adapting an object ``adaptee`` to a protocol ``to_protocol`` is based on a weighted graph. Nodes on the graphs are protocols (types or interfaces). Edges are adaptation offers that connect a ``offer.from_protocol`` to a ``offer.to_protocol``. Edges connect protocol ``A`` to protocol ``B`` and are weighted by two numbers in this priority: 1) a unit weight (1) representing the fact that we use 1 adaptation offer to go from ``A`` to ``B`` 2) the number of steps up the type hierarchy that we need to take to go from ``A`` to ``offer.from_protocol``, so that more specific adapters are always preferred The algorithm finds the shortest weighted path between ``adaptee`` and ``to_protocol``. Once a candidate path is found, it tries to create the chain of adapters using the factories in the adaptation offers that compose the path. If this fails because of conditional adaptation (i.e., an adapter factory returns None), the path is discarded and the algorithm looks for the next shortest path. Cycles in adaptation are avoided by only considering path were every adaptation offer is used at most once. .. _migration: Migration guide ``````````````` The implementation of the adaptation mechanism changed in Traits 4.4.0 from one based on PyProtocols to a new, smaller, and more robust implementation. Code written against ``traits.protocols`` will continue to work, although the `traits.protocols` API has been deprecated and its members will log a warning the first time they are accessed. The ``traits.protocols`` package will be removed in Traits 5.0 . This is a list of replacements for the old API: * :class:`traits.protocols.api.AdaptationFailure` Use :class:`traits.api.AdaptationError` instead. * :func:`traits.api.adapts` Use the :func:`traits.api.register_factory` function. * :func:`implements` Use the :func:`traits.api.provides` decorator instead. * :func:`traits.protocols.api.declareAdapter` Use the function :func:`traits.api.register_factory`, or the function :func:`traits.adaptation.api.register_offer` instead. It is no longer necessary to distinguish between "types", "protocols", and "objects". * :func:`traits.protocols.api.declareImplementation` This function was used occasionally to declare that an arbitrary type (e.g., ``dict``) implements an interface. Users that use Python ABCs can use the ``register`` method for achieving the same result. Otherwise, use the function :func:`traits.adaptation.api.register_provides` that declares a "null" adapter to adapt the type to the interface. * Testing if a class is an Interface ``issubclass(klass, Interface)`` is not reliable, use :func:`traits.api.isinterface` instead Gotchas ``````` 1) The adaptation mechanism does not explicitly support old-style classes. Adaptation might work in particular cases but is not guaranteed to work correctly in situations involving old-style classes. When used with Traits, the classes involved in adaptation are typically subclasses of :class:`~.HasTraits`, in which case this is not an issue. 2) The methods :func:`~traits.adaptation.adaptation_manager.register_factory`, :func:`~traits.adaptation.adaptation_manager.adapt`, etc. use a global adaptation manager, which is accessible through the function :func:`~traits.adaptation.adaptation_manager.get_global_adaptation_manager`. The traits automatic adaptation features also use the global manager. Having a global adaptation manager can get you into trouble, for the usual reasons related to having a global state. If you want to have more control over adaptation, we recommend creating a new :class:`~traits.adaptation.adaptation_manager.AdaptationManager` instance, use it directly in your application, and set it as the global manager using :func:`~traits.adaptation.adaptation_manager.set_global_adaptation_manager`. A common issue with the global manager arises in unittesting, where adapters registered in one test influence the outcome of other tests downstream. Tests relying on adaptation should make sure to reset the state of the global adapter using :func:`~traits.adaptation.adaptation_manager.reset_global_adaptation_manager`. Recommended readings about adaptation ````````````````````````````````````` This is a list of interesting readings about adaptation and the adapter pattern outside of Traits: * `PyProtocols `_, a precursor of ``traits.adaptation`` * `PEP 246 `_ on object adaptation * `Article about adapters in Eclipse plugins `_ .. index:: property traits .. _property-traits: Property Traits --------------- The predefined Property() trait factory function defines a Traits-based version of a Python property, with "getter" and "setter" methods. This type of trait provides a powerful technique for defining trait attributes whose values depend on the state of other object attributes. In particular, this can be very useful for creating synthetic trait attributes which are editable or displayable in a TraitUI view. .. _property-factory-function: Property Factory Function ````````````````````````` The Property() function has the following signature: .. function:: Property( [fget=None, fset=None, fvalidate=None, force=False, handler=None, trait=None, **metadata] ) All parameters are optional, including the *fget* "getter", *fvalidate* "validator" and *fset* "setter" methods. If no parameters are specified, then the trait looks for and uses methods on the same class as the attribute that the trait is assigned to, with names of the form _get_\ *name*\ (), _validate_\ *name*\ () and _set_\ *name*\ (), where *name* is the name of the trait attribute. If you specify a trait as either the *fget* parameter or the *trait* parameter, that trait's handler supersedes the *handler* argument, if any. Because the *fget* parameter accepts either a method or a trait, you can define a Property trait by simply passing another trait. For example:: source = Property( Code ) This line defines a trait whose value is validated by the Code trait, and whose getter and setter methods are defined elsewhere on the same class. If a Property trait has only a getter function, it acts as read-only; if it has only a setter function, it acts as write-only. It can lack a function due to two situations: * A function with the appropriate name is not defined on the class. * The *force* option is True, (which requires the Property() factory function to ignore functions on the class) and one of the access functions was not specified in the arguments. .. index:: property traits; caching value .. _caching-a-property-value: Caching a Property Value ```````````````````````` In some cases, the cost of computing the value of a property trait attribute may be very high. In such cases, it is a good idea to cache the most recently computed value, and to return it as the property value without recomputing it. When a change occurs in one of the attributes on which the cached value depends, the cache should be cleared, and the property value should be recomputed the next time its value is requested. .. index:: cached_property decorator, depends_on metadata One strategy to accomplish caching would be to use a private attribute for the cached value, and notification listener methods on the attributes that are depended on. However, to simplify the situation, Property traits support a @cached_property decorator and **depends_on** metadata. Use @cached_property to indicate that a getter method's return value should be cached. Use **depends_on** to indicate the other attributes that the property depends on. .. index:: examples; cached property For example:: # cached_prop.py -- Example of @cached_property decorator from traits.api import HasPrivateTraits, List, Int,\ Property, cached_property class TestScores ( HasPrivateTraits ): scores = List( Int ) average = Property( depends_on = 'scores' ) @cached_property def _get_average ( self ): s = self.scores return (float( reduce( lambda n1, n2: n1 + n2, s, 0 ) ) / len( s )) The @cached_property decorator takes no arguments. Place it on the line preceding the property's getter method. The **depends_on** metadata attribute accepts extended trait references, using the same syntax as the on_trait_change() method's name parameter, described in :ref:`the-name-parameter`. As a result, it can take values that specify attributes on referenced objects, multiple attributes, or attributes that are selected based on their metadata attributes. .. index:: persistence, __getstate__(), __setstate__() .. _persistence: Persistence ----------- In version 3.0, the Traits package provides __getstate__() and __setstate__() methods on HasTraits, to implement traits-aware policies for serialization and deserialization (i.e., pickling and unpickling). .. index:: HasTraits class; pickling, pickling HasTraits objects .. _pickling-hastraits-objects: Pickling HasTraits Objects `````````````````````````` Often, you may wish to control for a HasTraits subclass which parts of an instance's state are saved, and which are discarded. A typical approach is to define a __getstate__() method that copies the object's __dict__ attribute, and deletes those items that should not be saved. This approach works, but can have drawbacks, especially related to inheritance. .. index:: transient; metadata The HasTraits __getstate__() method uses a more generic approach, which developers can customize through the use of traits metadata attributes, often without needing to override or define a __getstate__() method in their application classes. In particular, the HasTraits __getstate__() method discards the values of all trait attributes that have the **transient** metadata attribute set to True, and saves all other trait attributes. So, to mark which trait values should not be saved, you set **transient** to True in the metadata for those trait attributes. The benefits of this approach are that you do not need to override __getstate__(), and that the metadata helps document the pickling behavior of the class. .. index:: examples; transient metadata For example:: # transient_metadata.py -- Example of using 'transient' metadata from traits.api import HasTraits, File, Any class DataBase ( HasTraits ): # The name of the data base file: file_name = File # The open file handle used to access the data base: file = Any( transient = True ) In this example, the DataBase class's file trait is marked as transient because it normally contains an open file handle used to access a data base. Since file handles typically cannot be pickled and restored, the file handle should not be saved as part of the object's persistent state. Normally, the file handle would be re-opened by application code after the object has been restored from its persisted state. .. index:: transient; predefined traits .. _predefined-transient-traits: Predefined Transient Traits ``````````````````````````` A number of the predefined traits in the Traits package are defined with **transient** set to True, so you do not need to explicitly mark them. The automatically transient traits are: * Constant * Event * Read-only and write-only Property traits (See :ref:`property-factory-function`) * Shadow attributes for mapped traits (See :ref:`mapped-traits`) * Private attributes of HasPrivateTraits subclasses (See :ref:`hasprivatetraits`) * Delegate traits that do not have a local value overriding the delegation. Delegate traits with a local value are non-transient, i.e., they are serialized. (See :ref:`delegatesto`) You can mark a Delegate trait as transient if you do not want its value to ever be serialized. .. index:: __getstate__(); overriding .. _overriding_getstate: Overriding __getstate__() ````````````````````````` In general, try to avoid overriding __getstate__() in subclasses of HasTraits. Instead, mark traits that should not be pickled with ``transient = True`` metadata. However, in cases where this strategy is insufficient, use the following pattern to override __getstate__() to remove items that should not be persisted:: def __getstate__ ( self ): state = super( XXX, self ).__getstate__() for key in [ 'foo', 'bar' ]: if key in state: del state[ key ] return state .. index:: unpickling HasTraits objects, HasTraits class; unpickling .. _unpicking-hastraits-objects: Unpickling HasTraits Objects ```````````````````````````` The __setstate__() method of HasTraits differs from the default Python behavior in one important respect: it explicitly sets the value of each attribute using the values from the state dictionary, rather than simply storing or copying the entire state dictionary to its **__dict__** attribute. While slower, this strategy has the advantage of generating trait change notifications for each attribute. These notifications are important for classes that rely on them to ensure that their internal object state remains consistent and up to date. .. note:: If you're manually creating state dictionaries for consumption by __setstate__(), you should be aware of an additional implementation detail: when pickling, the HasTraits __getstate__() method returns a dictionary with an extra ``'__traits_version__'`` key giving the version of Traits used at pickling time. If this key is not present when unpickling, the HasTraits __setstate__() method falls back to a compatibility mode and may not restore the state correctly. For the same reason, if you're overriding __getstate__(), you should be careful to make the appropriate ``super(..., self).__getstate__()`` call. .. index:: __setstate__(); overriding .. _overriding-setstate: Overriding __setstate__() ````````````````````````` You may wish to override the HasTraits __setstate__() method, for example for classes that do not need to receive trait change notifications, and where the overhead of explicitly setting each attribute is undesirable. You can override __setstate__() to update the object's __dict__ directly. However, in such cases, it is important ensure that trait notifications are properly set up so that later change notifications are handled. You can do this in two ways: * Call the __setstate__() super method (for example, with an empty state dictionary). * Call the HasTraits class's private _init_trait_listeners() method; this method has no parameters and does not return a result. .. index:: HasTraits class; methods .. _useful-methods-on-hastraits: Useful Methods on HasTraits --------------------------- The HasTraits class defines a number of methods, which are available to any class derived from it, i.e., any class that uses trait attributes. This section provides examples of a sampling of these methods. Refer to the *Traits API Reference* for a complete list of HasTraits methods. .. index:: add_trait() .. _add-trait: add_trait() ``````````` This method adds a trait attribute to an object dynamically, after the object has been created. For more information, see :ref:`per-object-trait-attributes`. .. index:: clone_traits() .. _clone-traits: clone_traits() `````````````` This method copies trait attributes from one object to another. It can copy specified attributes, all explicitly defined trait attributes, or all explicitly and implicitly defined trait attributes on the source object. This method is useful if you want to allow a user to edit a clone of an object, so that changes are made permanent only when the user commits them. In such a case, you might clone an object and its trait attributes; allow the user to modify the clone; and then re-clone only the trait attributes back to the original object when the user commits changes. .. index:: set() .. _set: set() ````` This method takes a list of keyword-value pairs, and sets the trait attribute corresponding to each keyword to the matching value. This shorthand is useful when a number of trait attributes need to be set on an object, or a trait attribute value needs to be set in a lambda function. For example:: person.trait_set(name='Bill', age=27) The statement above is equivalent to the following:: person.name = 'Bill' person.age = 27 .. index:: add_class_trait() .. _add-class-trait: add_class_trait() ````````````````` The add_class_trait() method is a class method, while the preceding HasTraits methods are instance methods. This method is very similar to the add_trait() instance method. The difference is that adding a trait attribute by using add_class_trait() is the same as having declared the trait as part of the class definition. That is, any trait attribute added using add_class_trait() is defined in every subsequently-created instance of the class, and in any subsequently-defined subclasses of the class. In contrast, the add_trait() method adds the specified trait attribute only to the object instance it is applied to. In addition, if the name of the trait attribute ends with a '_', then a new (or replacement) prefix rule is added to the class definition, just as if the prefix rule had been specified statically in the class definition. It is not possible to define new prefix rules using the add_trait() method. One of the main uses of the add_class_trait() method is to add trait attribute definitions that could not be defined statically as part of the body of the class definition. This occurs, for example, when two classes with trait attributes are being defined and each class has a trait attribute that should contain a reference to the other. For the class that occurs first in lexical order, it is not possible to define the trait attribute that references the other class, since the class it needs to refer to has not yet been defined. .. index:: pair: examples; add_class_trait() This is illustrated in the following example:: # circular_definition.py --- Non-working example of mutually- # referring classes from traits.api import HasTraits, Trait class Chicken(HasTraits): hatched_from = Trait(Egg) class Egg(HasTraits): created_by = Trait(Chicken) As it stands, this example will not run because the **hatched_from** attribute references the Egg class, which has not yet been defined. Reversing the definition order of the classes does not fix the problem, because then the **created_by** trait references the Chicken class, which has not yet been defined. The problem can be solved using the add_class_trait() method, as shown in the following code:: # add_class_trait.py --- Example of mutually-referring classes # using add_class_trait() from traits.api import HasTraits, Trait class Chicken(HasTraits): pass class Egg(HasTraits): created_by = Trait(Chicken) Chicken.add_class_trait('hatched_from', Egg) .. index:: performance of Traits .. _performance-considerations-of-traits: Performance Considerations of Traits ------------------------------------ Using traits can potentially impose a performance penalty on attribute access over and above that of normal Python attributes. For the most part, this penalty, if any, is small, because the core of the Traits package is written in C, just like the Python interpreter. In fact, for some common cases, subclasses of HasTraits can actually have the same or better performance than old or new style Python classes. However, there are a couple of performance-related factors to keep in mind when defining classes and attributes using traits: * Whether a trait attribute defers its value through delegation or prototyping * The complexity of a trait definition If a trait attribute does not defer its value, the performance penalty can be characterized as follows: * Getting a value: No penalty (i.e., standard Python attribute access speed or faster) * Setting a value: Depends upon the complexity of the validation tests performed by the trait definition. Many of the predefined trait handlers defined in the Traits package support fast C-level validation. For most of these, the cost of validation is usually negligible. For other trait handlers, with Python-level validation methods, the cost can be quite a bit higher. If a trait attribute does defer its value, the cases to be considered are: * Getting the default value: Cost of following the deferral chain. The chain is resolved at the C level, and is quite fast, but its cost is linear with the number of deferral links that must be followed to find the default value for the trait. * Getting an explicitly assigned value for a prototype: No penalty (i.e., standard Python attribute access speed or faster) * Getting an explicitly assigned value for a delegate: Cost of following the deferral chain. * Setting: Cost of following the deferral chain plus the cost of performing the validation of the new value. The preceding discussions about deferral chain following and fast versus slow validation apply here as well. In a typical application scenario, where attributes are read more often than they are written, and deferral is not used, the impact of using traits is often minimal, because the only cost occurs when attributes are assigned and validated. The worst case scenario occurs when deferral is used heavily, either for delegation, or for prototyping to provide attributes with default values that are seldom changed. In this case, the cost of frequently following deferral chains may impose a measurable performance detriment on the application. Of course, this is offset by the convenience and flexibility provided by the deferral model. As with any powerful tool, it is best to understand its strengths and weaknesses and apply that understanding in determining when use of the tool is justified and appropriate. traits-4.6.0/docs/source/traits_user_manual/custom.rst000066400000000000000000000504711300633736300232370ustar00rootroot00000000000000.. index:: custom traits .. _custom-traits: ============= Custom Traits ============= The predefined traits such as those described in :ref:`predefined-traits` are handy shortcuts for commonly used types. However, the Traits package also provides facilities for defining complex or customized traits: * Subclassing of traits * The Trait() factory function * Predefined or custom trait handlers .. index:: subclassing traits, TraitType class .. _trait-subclassing: Trait Subclassing ----------------- Starting with Traits version 3.0, most predefined traits are defined as subclasses of traits.trait_handlers.TraitType. As a result, you can subclass one of these traits, or TraitType, to derive new traits. Refer to the *Traits API Reference* to see whether a particular predefined trait derives from TraitType. .. index:: pair: subclassing traits; examples Here's an example of subclassing a predefined trait class:: # trait_subclass.py -- Example of subclassing a trait class from traits.api import BaseInt class OddInt ( BaseInt ): # Define the default value default_value = 1 # Describe the trait type info_text = 'an odd integer' def validate ( self, object, name, value ): value = super(OddInt, self).validate(object, name, value) if (value % 2) == 1: return value self.error( object, name, value ) The OddInt class defines a trait that must be an odd integer. It derives from BaseInt, rather than Int, as you might initially expect. BaseInt and Int are exactly the same, except that Int has a **fast_validate attribute**, which causes it to quickly check types at the C level, not go through the expense of executing the general validate() method. [6]_ As a subclass of BaseInt, OddInt can reuse and change any part of the BaseInt class behavior that it needs to. In this case, it reuses the BaseInt class's validate() method, via the call to super() in the OddInt validate() method. Further, OddInt is related to BaseInt, which can be useful as documentation, and in programming. You can use the subclassing strategy to define either a trait type or a trait property, depending on the specific methods and class constants that you define. A trait type uses a validate() method, while a trait property uses get() and set() methods. .. index: trait type; defining .. _defining-a-trait-type: Defining a Trait Type ````````````````````` The members that are specific to a trait type subclass are: .. index:: default_value attribute, get_default_value() * validate() method * post_setattr() method * **default_value** attribute or get_default_value() method Of these, only the validate() method must be overridden in trait type subclasses. A trait type uses a validate() method to determine the validity of values assigned to the trait. Optionally, it can define a post_setattr() method, which performs additional processing after a value has been validated and assigned. The signatures of these methods are: .. method:: validate( object, name, value ) .. method:: post_setattr( object, name, value ) The parameters of these methods are: .. index:: object parameter; validate(), name parameter; validate() .. index:: value parameter; validate() * *object*: The object whose trait attribute whose value is being assigned. * *name*: The name of the trait attribute whose value is being assigned. * *value*: The value being assigned. The validate() method returns either the original value or any suitably coerced or adapted value that is legal for the trait. If the value is not legal, and cannot be coerced or adapted to be legal, the method must either raise a TraitError, or calls the error() method to raise a TraitError on its behalf. The subclass can define a default value either as a constant or as a computed value. To use a constant, set the class-level **default_value attribute**. To compute the default value, override the TraitType class's get_default_value() method. .. index:: trait property; defining .. _defining-a-trait-property: Defining a Trait Property ````````````````````````` A trait property uses get() and set() methods to interact with the value of the trait. If a TraitType subclass contains a get() method or a set() method, any definition it might have for validate() is ignored. The signatures of these methods are: .. method:: get( object, name) .. method:: set( object, name, value) In these signatures, the parameters are: * *object*: The object that the property applies to. * *name*: The name of the trait property attribute on the object. * *value*: The value being assigned to the property. If only a get() method is defined, the property behaves as read-only. If only a set() method is defined, the property behaves as write-only. The get() method returns the value of the *name* property for the specified object. The set() method does not return a value, but will raise a TraitError if the specified *value* is not valid, and cannot be coerced or adapted to a valid value. .. index:: TraitType class; members .. _other-traittype-members: Other TraitType Members ``````````````````````` The following members can be specified for either a trait type or a trait property: .. index:: info_text attribute, info(), init(), create_editor() * **info_text** attribute or info() method * init() method * create_editor() method A trait must have an information string that describes the values accepted by the trait type (for example 'an odd integer'). Similarly to the default value, the subclass's information string can be either a constant string or a computed string. To use a constant, set the class-level info_text attribute. To compute the info string, override the TraitType class's info() method, which takes no parameters. If there is type-specific initialization that must be performed when the trait type is created, you can override the init() method. This method is automatically called from the __init__() method of the TraitType class. If you want to specify a default TraitsUI editor for the new trait type, you can override the create_editor() method. This method has no parameters, and returns the default trait editor to use for any instances of the type. For complete details on the members that can be overridden, refer to the *Traits API Reference* sections on the TraitType and BaseTraitHandler classes. .. index:: Trait() .. _the-trait-factory-function: The Trait() Factory Function ---------------------------- The Trait() function is a generic factory for trait definitions. It has many forms, many of which are redundant with the predefined shortcut traits. For example, the simplest form Trait(default_value), is equivalent to the functions for simple types described in :ref:`predefined-traits-for-simple-types`. For the full variety of forms of the Trait() function, refer to the *Traits API Reference*. The most general form of the Trait() function is: .. currentmodule:: traits.traits .. function:: Trait(default_value, {type | constant_value | dictionary | class | function | trait_handler | trait }+ ) :noindex: .. index:: compound traits The notation ``{ | | }+`` means a list of one or more of any of the items listed between the braces. Thus, this form of the function consists of a default value, followed by one or more of several possible items. A trait defined with multiple items is called a compound trait. When more than one item is specified, a trait value is considered valid if it meets the criteria of at least one of the items in the list. .. index:: pair: Trait() function; examples The following is an example of a compound trait with multiple criteria:: # compound.py -- Example of multiple criteria in a trait definition from traits.api import HasTraits, Trait, Range class Die ( HasTraits ): # Define a compound trait definition: value = Trait( 1, Range( 1, 6 ), 'one', 'two', 'three', 'four', 'five', 'six' ) The Die class has a **value trait**, which has a default value of 1, and can have any of the following values: * An integer in the range of 1 to 6 * One of the following strings: 'one', 'two', 'three', 'four', 'five', 'six' .. index:: Trait(); parameters .. _trait-parameters: Trait () Parameters ``````````````````` The items listed as possible arguments to the Trait() function merit some further explanation. .. index:: type; parameter to Trait(), constant_value parameter to Trait() .. index:: dictionary parameter to Trait(), class parameter to Trait() .. index:: function parameter to Trait(), trait handler; parameter to Trait() .. index:: trait; parameter to Trait() * *type*: See :ref:`type`. * *constant_value*: See :ref:`constant-value`. * *dictionary*: See :ref:`mapped-traits`. * *class*: Specifies that the trait value must be an instance of the specified class or one of its subclasses. * *function*: A "validator" function that determines whether a value being assigned to the attribute is a legal value. Traits version 3.0 provides a more flexible approach, which is to subclass an existing trait (or TraitType) and override the validate() method. * *trait_handler*: See :ref:`trait-handlers`. * *trait*: Another trait object can be passed as a parameter; any value that is valid for the specified trait is also valid for the trait referencing it. .. index:: type; parameter to Trait() .. _type: Type :::: A *type* parameter to the Trait() function can be any of the following standard Python types: * str or StringType * unicode or UnicodeType * int or IntType * long or LongType * float or FloatType * complex or ComplexType * bool or BooleanType * list or ListType * tuple or TupleType * dict or DictType * FunctionType * MethodType * ClassType * InstanceType * TypeType * NoneType Specifying one of these types means that the trait value must be of the corresponding Python type. .. index:: constant_value parameter to Trait() .. _constant-value: Constant Value :::::::::::::: A *constant_value* parameter to the Trait() function can be any constant belonging to one of the following standard Python types: * NoneType * int * long * float * complex * bool * str * unicode Specifying a constant means that the trait can have the constant as a valid value. Passing a list of constants to the Trait() function is equivalent to using the Enum predefined trait. .. index:: mapped traits .. _mapped-traits: Mapped Traits ````````````` If the Trait() function is called with parameters that include one or more dictionaries, then the resulting trait is called a "mapped" trait. In practice, this means that the resulting object actually contains two attributes: .. index:: shadow values * An attribute whose value is a key in the dictionary used to define the trait. * An attribute containing its corresponding value (i.e., the mapped or "shadow" value). The name of the shadow attribute is simply the base attribute name with an underscore appended. Mapped traits can be used to allow a variety of user-friendly input values to be mapped to a set of internal, program-friendly values. .. index:: mapped traits; examples The following examples illustrates mapped traits that map color names to tuples representing red, green, blue, and transparency values:: # mapped.py --- Example of a mapped trait from traits.api import HasTraits, Trait standard_color = Trait ('black', {'black': (0.0, 0.0, 0.0, 1.0), 'blue': (0.0, 0.0, 1.0, 1.0), 'cyan': (0.0, 1.0, 1.0, 1.0), 'green': (0.0, 1.0, 0.0, 1.0), 'magenta': (1.0, 0.0, 1.0, 1.0), 'orange': (0.8, 0.196, 0.196, 1.0), 'purple': (0.69, 0.0, 1.0, 1.0), 'red': (1.0, 0.0, 0.0, 1.0), 'violet': (0.31, 0.184, 0.31, 1.0), 'yellow': (1.0, 1.0, 0.0, 1.0), 'white': (1.0, 1.0, 1.0, 1.0), 'transparent': (1.0, 1.0, 1.0, 0.0) } ) red_color = Trait ('red', standard_color) class GraphicShape (HasTraits): line_color = standard_color fill_color = red_color The GraphicShape class has two attributes: **line_color** and **fill_color**. These attributes are defined in terms of the **standard_color** trait, which uses a dictionary. The **standard_color** trait is a mapped trait, which means that each GraphicShape instance has two shadow attributes: **line_color_** and **fill_color_**. Any time a new value is assigned to either **line_color** or **fill_color**, the corresponding shadow attribute is updated with the value in the dictionary corresponding to the value assigned. For example:: >>> import mapped >>> my_shape1 = mapped.GraphicShape() >>> print my_shape1.line_color, my_shape1.fill_color black red >>> print my_shape1.line_color_, my_shape1.fill_color_ (0.0, 0.0, 0.0, 1.0) (1.0, 0.0, 0.0, 1.0) >>> my_shape2 = mapped.GraphicShape() >>> my_shape2.line_color = 'blue' >>> my_shape2.fill_color = 'green' >>> print my_shape2.line_color, my_shape2.fill_color blue green >>> print my_shape2.line_color_, my_shape2.fill_color_ (0.0, 0.0, 1.0, 1.0) (0.0, 1.0, 0.0, 1.0) This example shows how a mapped trait can be used to create a user-friendly attribute (such as **line_color**) and a corresponding program-friendly shadow attribute (such as **line_color_**). The shadow attribute is program-friendly because it is usually in a form that can be directly used by program logic. There are a few other points to keep in mind when creating a mapped trait: * If not all values passed to the Trait() function are dictionaries, the non-dictionary values are copied directly to the shadow attribute (i.e., the mapping used is the identity mapping). * Assigning directly to a shadow attribute (the attribute with the trailing underscore in the name) is not allowed, and raises a TraitError. The concept of a mapped trait extends beyond traits defined via a dictionary. Any trait that has a shadow value is a mapped trait. For example, for the Expression trait, the assigned value must be a valid Python expression, and the shadow value is the compiled form of the expression. .. index:: trait handler; classes .. _trait-handlers: Trait Handlers -------------- In some cases, you may want to define a customized trait that is unrelated to any predefined trait behavior, or that is related to a predefined trait that happens to not be derived from TraitType. The option for such cases is to use a trait handler, either a predefined one or a custom one that you write. .. index:: TraitHandler class A trait handler is an instance of the traits.trait_handlers.TraitHandler class, or of a subclass, whose task is to verify the correctness of values assigned to object traits. When a value is assigned to an object trait that has a trait handler, the trait handler's validate() method checks the value, and assigns that value or a computed value, or raises a TraitError if the assigned value is not valid. Both TraitHandler and TraitType derive from BaseTraitHandler; TraitHandler has a more limited interface. The Traits package provides a number of predefined TraitHandler subclasses. A few of the predefined trait handler classes are described in the following sections. These sections also demonstrate how to define a trait using a trait handler and the Trait() factory function. For a complete list and descriptions of predefined TraitHandler subclasses, refer to the *Traits API Reference*, in the section on the traits.trait_handlers module. .. index:: TraitPrefixList class .. _traitprefixlist: TraitPrefixList ``````````````` The TraitPrefixList handler accepts not only a specified set of strings as values, but also any unique prefix substring of those values. The value assigned to the trait attribute is the full string that the substring matches. .. index:: pair: TraitPrefixList class; examples For example:: >>> from traits.api import HasTraits, Trait >>> from traits.api import TraitPrefixList >>> class Alien(HasTraits): ... heads = Trait('one', TraitPrefixList(['one','two','three'])) ... >>> alf = Alien() >>> alf.heads = 'o' >>> print alf.heads one >>> alf.heads = 'tw' >>> print alf.heads two >>> alf.heads = 't' # Error, not a unique prefix Traceback (most recent call last): File "", line 1, in File "c:\svn\ets3\traits_3.0.3\enthought\traits\trait_handlers.py", line 1802, in validate self.error( object, name, value ) File "c:\svn\ets3\traits_3.0.3\enthought\traits\trait_handlers.py", line 175, in error value ) traits.trait_errors.TraitError: The 'heads' trait of an Alien instance must be 'one' or 'two' or 'three' (or any unique prefix), but a value of 't' was specified. .. index:: TraitPrefixMap class .. _traitprefixmap: TraitPrefixMap `````````````` The TraitPrefixMap handler combines the TraitPrefixList with mapped traits. Its constructor takes a parameter that is a dictionary whose keys are strings. A string is a valid value if it is a unique prefix for a key in the dictionary. The value assigned is the dictionary value corresponding to the matched key. .. index:: pair: TraitPrefixMap class; examples The following example uses TraitPrefixMap to define a Boolean trait that accepts any prefix of 'true', 'yes', 'false', or 'no', and maps them to 1 or 0. :: # traitprefixmap.py --- Example of using the TraitPrefixMap handler from traits.api import Trait, TraitPrefixMap boolean_map = Trait('true', TraitPrefixMap( { 'true': 1, 'yes': 1, 'false': 0, 'no': 0 } ) ) .. index:: handler classes; custom .. _custom-trait-handlers: Custom Trait Handlers --------------------- If you need a trait that cannot be defined using a predefined trait handler class, you can create your own subclass of TraitHandler. The constructor (i.e., __init__() method) for your TraitHandler subclass can accept whatever additional information, if any, is needed to completely specify the trait. The constructor does not need to call the TraitHandler base class's constructor. The only method that a custom trait handler must implement is validate(). Refer to the *Traits API Reference* for details about this function. .. index:: pair: custom trait handler; examples .. _example-custom-trait-handler: Example Custom Trait Handler ```````````````````````````` The following example defines the OddInt trait (also implemented as a trait type in :ref:`defining-a-trait-type`) using a TraitHandler subclass. :: # custom_traithandler.py --- Example of a custom TraitHandler import types from traits.api import TraitHandler class TraitOddInteger(TraitHandler): def validate(self, object, name, value): if ((type(value) is types.IntType) and (value > 0) and ((value % 2) == 1)): return value self.error(object, name, value) def info(self): return '**a positive odd integer**' An application could use this new trait handler to define traits such as the following:: # use_custom_th.py --- Example of using a custom TraitHandler from traits.api import HasTraits, Trait, TraitRange from custom_traithandler import TraitOddInteger class AnOddClass(HasTraits): oddball = Trait(1, TraitOddInteger()) very_odd = Trait(-1, TraitOddInteger(), TraitRange(-10, -1)) The following example demonstrates why the info() method returns a phrase rather than a complete sentence:: >>> from use_custom_th import AnOddClass >>> odd_stuff = AnOddClass() >>> odd_stuff.very_odd = 0 Traceback (most recent call last): File "test.py", line 25, in ? odd_stuff.very_odd = 0 File "C:\wrk\src\lib\enthought\traits\traits.py", line 1119, in validate raise TraitError, excp traits.traits.TraitError: The 'very_odd' trait of an AnOddClass instance must be **a positive odd integer** or -10 <= an integer <= -1, but a value of 0 was specified. Note the emphasized result returned by the info() method, which is embedded in the exception generated by the invalid assignment. .. rubric:: Footnotes .. [6] All of the basic predefined traits (such as Float and Str) have a BaseType version that does not have the **fast_validate** attribute. traits-4.6.0/docs/source/traits_user_manual/debugging.rst000066400000000000000000000132051300633736300236520ustar00rootroot00000000000000.. index:: debugging, exceptions, trace trait change events ========================= Tips for debugging Traits ========================= Re-raising exceptions in change handlers ======================================== Traits will typically log (instead of raise) exceptions when an exception is encountered in a trait-change handler. This behavior is often preferred in applications, since you usually want to avoid critical failures in applications. However, when debugging these errors, the ``logging.Logger.exception`` only displays the tip of the traceback. For example, the following code changes a ``constant``: .. code-block:: python from traits.api import HasTraits, Int class Curmudgeon(HasTraits): constant = Int(1) def _constant_changed(self): raise ValueError() c = Curmudgeon() c.constant = 42 The ``constant`` trait-change handler raises an exception that is caught and logged:: Exception occurred in traits notification handler. Please check the log file for details. Exception occurred in traits notification handler for object: <__main__.Curmudgeon object at 0x107603050>, trait: constant, old value: 0, new value: 42 ... File "curmudgeon.py", line 12, in _constant_changed raise ValueError() ValueError This logged exception, however, only contains the tip of the traceback. This makes debugging a bit difficult. You can force exceptions to be re-raised by adding a custom exception handler: .. code-block:: python from traits.api import push_exception_handler push_exception_handler(reraise_exceptions=True) (For example, you could add this to the top of the original code block.) Re-running the original code example with this custom handler will now raise the following traceback:: Traceback (most recent call last): File "curmudgeon.py", line 15, in c.constant = 42 ... File "curmudgeon.py", line 12, in _constant_changed raise ValueError() ValueError Notice that this traceback has information about *where* we changed ``constant``. Note: This is a toy example; use ``Constant`` from ``traits.api`` if you actually want a constant trait. Tracing Traits Change Events ============================ Occasionally it is necessary to find the chain of event dispatches in traits classes. To help with debugging, a |record_events| context manager is provided in mod:`traits.util.event_tracer`. Trait change events taking place inside the context block will be recorded in a change event container (see example below) and can be saved to files (a file for each thread) for further inspection. Example: .. code-block:: python from traits.api import * from traits.util.event_tracer import record_events class MyModel(HasTraits): number = Float(2.0) list_of_numbers = List(Float()) count = Int(0) @on_trait_change('number') def _add_number_to_list(self, value): self.list_of_numbers.append(value) @on_trait_change('list_of_numbers[]') def _count_items(self): self.count = len(self.list_on_numbers) def add_to_number(self, value): self.number += value my_model = MyModel() with record_events() as change_event_container: my_model.number = 4.7 my_model.number = 3 # save files locally change_event_container.save_to_directory('./') Running the above example will write a file named MAinThread.trace in the local folder. The file contents will be similar to the lines below:: 2014-03-21 14:11:20.779000 -> 'number' changed from 2.0 to 4.7 in 'MyModel' 2014-03-21 14:11:20.779000 CALLING: '_add_number_to_list' in example.py 2014-03-21 14:11:20.780000 ---> 'list_of_numbers_items' changed from to in 'MyModel' 2014-03-21 14:11:20.780000 CALLING: 'handle_list_items_special' in C:\Users\itziakos\Projects\traits\traits\traits_listener.py 2014-03-21 14:11:20.780000 -----> 'list_of_numbers_items' changed from [] to [4.7] in 'MyModel' 2014-03-21 14:11:20.780000 CALLING: '_count_items' in exampler.py 2014-03-21 14:11:20.780000 -------> 'trait_added' changed from to 'list_on_numbers' in 'MyModel' 2014-03-21 14:11:20.780000 CALLING: '_trait_added_changed' in C:\Users\itziakos\Projects\traits\traits\has_traits.py 2014-03-21 14:11:20.780000 <------- EXIT: '_trait_added_changed' 2014-03-21 14:11:20.780000 <----- EXIT: '_count_items' [EXCEPTION: 'MyModel' object has no attribute 'list_on_numbers'] 2014-03-21 14:11:20.780000 <--- EXIT: 'handle_list_items_special' 2014-03-21 14:11:20.781000 <- EXIT: '_add_number_to_list' 2014-03-21 14:11:20.781000 -> 'number' changed from 4.7 to 3.0 in 'MyModel' 2014-03-21 14:11:20.781000 CALLING: '_add_number_to_list' in example.py 2014-03-21 14:11:20.781000 ---> 'list_of_numbers_items' changed from to in 'MyModel' 2014-03-21 14:11:20.781000 CALLING: 'handle_list_items_special' in C:\Users\itziakos\Projects\traits\traits\traits_listener.py 2014-03-21 14:11:20.781000 -----> 'list_of_numbers_items' changed from [] to [3.0] in 'MyModel' 2014-03-21 14:11:20.781000 CALLING: '_count_items' in example.py 2014-03-21 14:11:20.781000 <----- EXIT: '_count_items' [EXCEPTION: 'MyModel' object has no attribute 'list_on_numbers'] 2014-03-21 14:11:20.782000 <--- EXIT: 'handle_list_items_special' 2014-03-21 14:11:20.782000 <- EXIT: '_add_number_to_list' .. |record_events| replace:: :func:`~traits.util.event_tracer.record_events` traits-4.6.0/docs/source/traits_user_manual/deferring.rst000066400000000000000000000255161300633736300236740ustar00rootroot00000000000000.. index:: deferral .. _deferring-traits: Deferring Trait Definitions =========================== One of the advanced capabilities of the Traits package is its support for trait attributes to defer their definition and value to another object than the one the attribute is defined on. This has many applications, especially in cases where objects are logically contained within other objects and may wish to inherit or derive some attributes from the object they are contained in or associated with. Deferring leverages the common "has-a" relationship between objects, rather than the "is-a" relationship that class inheritance provides. .. index:: delegation, prototyping There are two ways that a trait attribute can defer to another object's attribute: *delegation* and *prototyping*. In delegation, the deferring attribute is a complete reflection of the delegate attribute. Both the value and validation of the delegate attribute are used for the deferring attribute; changes to either one are reflected in both. In prototyping, the deferring attribute gets its value and validation from the prototype attribute, *until the deferring attribute is explicitly changed*. At that point, while the deferring attribute still uses the prototype's validation, the link between the values is broken, and the two attributes can change independently. This is essentially a "copy on write" scheme. The concepts of delegation and prototyping are implemented in the Traits package by two classes derived from TraitType: DelegatesTo and PrototypedFrom. [5]_ .. _delegatesto: DelegatesTo ----------- .. class:: DelegatesTo(delegate[, prefix='', listenable=True, **metadata]) .. index:: delegate parameter to DelegatesTo initializer The *delegate* parameter is a string that specifies the name of an attribute on the same object, which refers to the object whose attribute is deferred to; it is usually an Instance trait. The value of the delegating attribute changes whenever: * The value of the appropriate attribute on the delegate object changes. * The object referenced by the trait named in the *delegate* parameter changes. * The delegating attribute is explicitly changed. Changes to the delegating attribute are propagated to the delegate object's attribute. The *prefix* and *listenable* parameters to the initializer function specify additional information about how to do the delegation. .. index:: pair: delegation; examples If *prefix* is the empty string or omitted, the delegation is to an attribute of the delegate object with the same name as the trait defined by the DelegatesTo object. Consider the following example:: # delegate.py --- Example of trait delegation from traits.api \ import DelegatesTo, HasTraits, Instance, Str class Parent(HasTraits): first_name = Str last_name = Str class Child(HasTraits): first_name = Str last_name = DelegatesTo('father') father = Instance(Parent) mother = Instance(Parent) """ >>> tony = Parent(first_name='Anthony', last_name='Jones') >>> alice = Parent(first_name='Alice', last_name='Smith') >>> sally = Child( first_name='Sally', father=tony, mother=alice) >>> print sally.last_name Jones >>> sally.last_name = 'Cooper' # Updates delegatee >>> print tony.last_name Cooper >>> sally.last_name = sally.mother # ERR: string expected Traceback (most recent call last): File "", line 1, in ? File "c:\src\trunk\enthought\traits\trait_handlers.py", line 163, in error raise TraitError, ( object, name, self.info(), value ) traits.trait_errors.TraitError: The 'last_name' trait of a Parent instance must be a string, but a value of <__main__.Parent object at 0x014D6D80> was specified. """ A Child object delegates its **last_name** attribute value to its **father** object's **last_name** attribute. Because the *prefix* parameter was not specified in the DelegatesTo initializer, the attribute name on the delegatee is the same as the original attribute name. Thus, the **last_name** of a Child is the same as the **last_name** of its **father**. When either the **last_name** of the Child or the **last_name** of the father is changed, both attributes reflect the new value. .. _prototypedfrom: PrototypedFrom -------------- .. class:: PrototypedFrom(prototype[, prefix='', listenable=True, **metadata]) .. index:: prototype parameter to PrototypesFrom The *prototype* parameter is a string that specifies the name of an attribute on the same object, which refers to the object whose attribute is prototyped; it is usually an Instance trait. The prototyped attribute behaves similarly to a delegated attribute, until it is explicitly changed; from that point forward, the prototyped attribute changes independently from its prototype. The *prefix* and *listenable* parameters to the initializer function specify additional information about how to do the prototyping. .. _keyword-parameters: Keyword Parameters ------------------ The *prefix* and *listenable* parameters of the DelegatesTo and PrototypedFrom initializer functions behave similarly for both classes. .. index:: prefix parameter to initializer methods .. _prefix-keyword: Prefix Keyword `````````````` When the *prefix* parameter is a non-empty string, the rule for performing trait attribute look-up in the deferred-to object is modified, with the modification depending on the format of the prefix string: * If *prefix* is a valid Python attribute name, then the original attribute name is replaced by prefix when looking up the deferred-to attribute. * If *prefix* ends with an asterisk ('*'), and is longer than one character, then *prefix*, minus the trailing asterisk, is added to the front of the original attribute name when looking up the object attribute. * If *prefix* is equal to a single asterisk ('*'), the value of the object class's **__prefix__** attribute is added to the front of the original attribute name when looking up the object attribute. .. index:: single: examples; prototype prefix pair: examples; prototyping Each of these three possibilities is illustrated in the following example, using PrototypedFrom:: # prototype_prefix.py --- Examples of PrototypedFrom() # prefix parameter from traits.api import \ PrototypedFrom, Float, HasTraits, Instance, Str class Parent (HasTraits): first_name = Str family_name = '' favorite_first_name = Str child_allowance = Float(1.00) class Child (HasTraits): __prefix__ = 'child_' first_name = PrototypedFrom('mother', 'favorite_*') last_name = PrototypedFrom('father', 'family_name') allowance = PrototypedFrom('father', '*') father = Instance(Parent) mother = Instance(Parent) """ >>> fred = Parent( first_name = 'Fred', family_name = 'Lopez', \ ... favorite_first_name = 'Diego', child_allowance = 5.0 ) >>> maria = Parent(first_name = 'Maria', family_name = 'Gonzalez',\ ... favorite_first_name = 'Tomas', child_allowance = 10.0 ) >>> nino = Child( father=fred, mother=maria ) >>> print '%s %s gets $%.2f for allowance' % (nino.first_name, \ ... nino.last_name, nino.allowance) Tomas Lopez gets $5.00 for allowance """ In this example, instances of the Child class have three prototyped trait attributes: * **first_name**, which prototypes from the **favorite_first_name** attribute of its **mother** object. * **last_name**, which prototyped from the **family_name attribute** of its **father** object. * **allowance**, which prototypes from the **child_allowance** attribute of its **father** object. .. index:: listenable parameter to initializer methods .. _listenable-keyword: Listenable Keyword `````````````````` By default, you can attach listeners to deferred trait attributes, just as you can attach listeners to most other trait attributes, as described in the following section. However, implementing the notifications correctly requires hooking up complicated listeners under the covers. Hooking up these listeners can be rather more expensive than hooking up other listeners. Since a common use case of deferring is to have a large number of deferred attributes for static object hierarchies, this feature can be turned off by setting ``listenable=False`` in order to speed up instantiation. .. index:: single: deferral; notification with pair: examples; deferral .. _notification-with-deferring: Notification with Deferring --------------------------- While two trait attributes are linked by a deferring relationship (either delegation, or prototyping before the link is broken), notifications for changes to those attributes are linked as well. When the value of a deferred-to attribute changes, notification is sent to any handlers on the deferring object, as well as on the deferred-to object. This behavior is new in Traits version 3.0. In previous versions, only handlers for the deferred-to object (the object directly changed) were notified. This behavior is shown in the following example:: # deferring_notification.py -- Example of notification with deferring from traits.api \ import HasTraits, Instance, PrototypedFrom, Str class Parent ( HasTraits ): first_name = Str last_name = Str def _last_name_changed(self, new): print "Parent's last name changed to %s." % new class Child ( HasTraits ): father = Instance( Parent ) first_name = Str last_name = PrototypedFrom( 'father' ) def _last_name_changed(self, new): print "Child's last name changed to %s." % new """ >>> dad = Parent( first_name='William', last_name='Chase' ) Parent's last name changed to Chase. >>> son = Child( first_name='John', father=dad ) Child's last name changed to Chase. >>> dad.last_name='Jones' Parent's last name changed to Jones. Child's last name changed to Jones. >>> son.last_name='Thomas' Child's last name changed to Thomas. >>> dad.last_name='Riley' Parent's last name changed to Riley. >>> del son.last_name Child's last name changed to Riley. >>> dad.last_name='Simmons' Parent's last name changed to Simmons. Child's last name changed to Simmons. """ Initially, changing the last name of the father triggers notification on both the father and the son. Explicitly setting the son's last name breaks the deferring link to the father; therefore changing the father's last name does not notify the son. When the son reverts to using the father's last name (by deleting the explicit value), changes to the father's last name again affect and notif .. rubric:: Footnotes .. [5] Both of these class es inherit from the Delegate class. Explicit use of Delegate is deprecated, as its name and default behavior (prototyping) are incongruous. traits-4.6.0/docs/source/traits_user_manual/defining.rst000066400000000000000000001047741300633736300235160ustar00rootroot00000000000000.. index:: validation, using traits .. _defining-traits-initialization-and-validation: ============================================== Defining Traits: Initialization and Validation ============================================== Using the Traits package in a Python program involves the following steps: .. index:: importing Traits names, traits.api; importing from 1. Import the names you need from the Traits package traits.api. 2. Define the traits you want to use. .. index:: HasTraits class 3. Define classes derived from HasTraits (or a subclass of HasTraits), with attributes that use the traits you have defined. In practice, steps 2 and 3 are often combined by defining traits in-line in an attribute definition. This strategy is used in many examples in this guide. However, you can also define traits independently, and reuse the trait definitions across multiple classes and attributes (see :ref:`reusing-trait-definitions`). In order to use trait attributes in a class, the class must inherit from the HasTraits class in the Traits package (or from a subclass of HasTraits). The following example defines a class called Person that has a single trait attribute **weight**, which is initialized to 150.0 and can only take floating point values. .. index:: single: examples; minimal :: # minimal.py --- Minimal example of using traits. from traits.api import HasTraits, Float class Person(HasTraits): weight = Float(150.0) .. index:: attribute definition In this example, the attribute named **weight** specifies that the class has a corresponding trait called **weight**. The value associated with the attribute **weight** (i.e., ``Float(150.0)``) specifies a predefined trait provided with the Traits package, which requires that values assigned be of the standard Python type **float**. The value 150.0 specifies the default value of the trait. The value associated with each class-level attribute determines the characteristics of the instance attribute identified by the attribute name. For example:: >>> from minimal import Person >>> # instantiate the class >>> joe = Person() >>> # Show the default value >>> joe.weight 150.0 >>> # Assign new values >>> joe.weight = 161.9 # OK to assign a float >>> joe.weight = 162 # OK to assign an int >>> joe.weight = 'average' # Error to assign a string Traceback (most recent call last): ... traits.trait_errors.TraitError: The 'weight' trait of a Person instance must be a float, but a value of 'average' was specified. In this example, **joe** is an instance of the Person class defined in the previous example. The **joe** object has an instance attribute **weight**, whose initial value is the default value of the Person.weight trait (150.0), and whose assignment is governed by the Person.weight trait's validation rules. Assigning an integer to **weight** is acceptable because there is no loss of precision (but assigning a float to an Int trait would cause an error). The Traits package allows creation of a wide variety of trait types, ranging from very simple to very sophisticated. The following section presents some of the simpler, more commonly used forms. .. index:: predefined traits .. _predefined-traits: Predefined Traits ----------------- The Traits package includes a large number of predefined traits for commonly used Python data types. In the simplest case, you can assign the trait name to an attribute of a class derived from HasTraits; any instances of the class will have that attribute initialized to the built-in default value for the trait. For example:: account_balance = Float This statement defines an attribute whose value must be a floating point number, and whose initial value is 0.0 (the built-in default value for Floats). If you want to use an initial value other than the built-in default, you can pass it as an argument to the trait:: account_balance = Float(10.0) Most predefined traits are callable, [2]_ and can accept a default value and possibly other arguments; all that are callable can also accept metadata as keyword arguments. (See :ref:`other-predefined-traits` for information on trait signatures, and see :ref:`trait-metadata` for information on metadata arguments.) .. index:: simple types .. _predefined-traits-for-simple-types: Predefined Traits for Simple Types `````````````````````````````````` There are two categories of predefined traits corresponding to Python simple types: those that coerce values, and those that cast values. These categories vary in the way that they handle assigned values that do not match the type explicitly defined for the trait. However, they are similar in terms of the Python types they correspond to, and their built-in default values, as listed in the following table. .. index:: pair: types; casting pair: types; coercing pair: plain; integer type pair: long; integer type pair: type; string pair: type; Unicode .. index:: Boolean type, Bool trait, CBool trait, Complex trait, CComplex trait .. index:: complex number type, Float trait, CFloat trait, Int trait, CInt trait .. index:: floating point number type, Long trait, CLong trait, Str trait .. index:: CStr trait, Unicode; trait, CUnicode trait, Bytes trait, CBytes trait .. _predefined-defaults-for-simple-types-table: .. rubric:: Predefined defaults for simple types ============== ============= ====================== ====================== Coercing Trait Casting Trait Python Type Built-in Default Value ============== ============= ====================== ====================== Bool CBool Boolean False Complex CComplex Complex number 0+0j Float CFloat Floating point number 0.0 Int CInt Plain integer 0 Long CLong Long integer 0L Str CStr String '' Unicode CUnicode Unicode u'' Bytes CBytes Bytes b'' ============== ============= ====================== ====================== .. index:: pair: types; coercing .. _trait-type-coercion: Trait Type Coercion ::::::::::::::::::: For trait attributes defined using the predefined "coercing" traits, if a value is assigned to a trait attribute that is not of the type defined for the trait, but it can be coerced to the required type, then the coerced value is assigned to the attribute. If the value cannot be coerced to the required type, a TraitError exception is raised. Only widening coercions are allowed, to avoid any possible loss of precision. The following table lists traits that coerce values, and the types that each coerces. .. index:: pair: types; coercing .. _type-coercions-permitted-for-coercing-traits-table: .. rubric:: Type coercions permitted for coercing traits ============= =========================================== Trait Coercible Types ============= =========================================== Complex Floating point number, plain integer Float Plain integer Long Plain integer Unicode String ============= =========================================== .. index:: pair: types; casting .. _trait-type-casting: Trait Type Casting :::::::::::::::::: For trait attributes defined using the predefined "casting" traits, if a value is assigned to a trait attribute that is not of the type defined for the trait, but it can be cast to the required type, then the cast value is assigned to the attribute. If the value cannot be cast to the required type, a TraitError exception is raised. Internally, casting is done using the Python built-in functions for type conversion: * bool() * complex() * float() * int() * str() * unicode() * bytes() .. index:: single: examples; coercing vs. casting The following example illustrates the difference between coercing traits and casting traits:: >>> from traits.api import HasTraits, Float, CFloat >>> class Person ( HasTraits ): ... weight = Float ... cweight = CFloat >>> >>> bill = Person() >>> bill.weight = 180 # OK, coerced to 180.0 >>> bill.cweight = 180 # OK, cast to float(180) >>> bill.weight = '180' # Error, invalid coercion Traceback (most recent call last): ... traits.trait_errors.TraitError: The 'weight' trait of a Person instance must be a float, but a value of '180' was specified. >>> bill.cweight = '180' # OK, cast to float('180') >>> print bill.cweight 180.0 >>> .. _other-predefined-traits: Other Predefined Traits ``````````````````````` The Traits package provides a number of other predefined traits besides those for simple types, corresponding to other commonly used data types; these predefined traits are listed in the following table. Refer to the *Traits API Reference*, in the section for the module traits.traits, for details. Most can be used either as simple names, which use their built-in default values, or as callables, which can take additional arguments. If the trait cannot be used as a simple name, it is omitted from the Name column of the table. .. index:: Any(), Array(), Button(), Callable(), CArray(), Class(), Code() .. index:: Color(), CSet(), Constant(), Dict() .. index:: Directory(), Disallow, Either(), Enum() .. index:: Event(), Expression(), false, File(), Font() .. index:: Instance(), List(), Method(), Module() .. index:: Password(), Property(), Python() .. index:: PythonValue(), Range(), ReadOnly(), Regex() .. index:: RGBColor(), Set() String(), This, .. index:: ToolbarButton(), true, Tuple(), Type() .. index:: undefined, UUID(), ValidatedTuple(), WeakRef() .. _predefined-traits-beyond-simple-types-table: .. rubric:: Predefined traits beyond simple types +------------------+----------------------------------------------------------+ | Name | Callable Signature | +==================+==========================================================+ | Any | Any( [*value* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Array | Array( [*dtype* = None, *shape* = None, *value* = None, | | | *typecode* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | ArrayOrNone | ArrayOrNone( [*dtype* = None, *shape* = None, | | | *value* = None, *typecode* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Button | Button( [*label* = '', *image* = None, *style* = | | | 'button', *orientation* = 'vertical', *width_padding* = | | | 7, *height_padding* = 5, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Callable | Callable( [*value* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | CArray | CArray( [*dtype* = None, *shape* = None, *value* = None, | | | *typecode* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Class | Class( [*value*, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Code | Code( [*value* = '', *minlen* = 0, *maxlen* = sys.maxint,| | | *regex* = '', \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Color | Color( [\*\ *args*, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | CSet | CSet( [*trait* = None, *value* = None, *items* = True, | | | \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | n/a | Constant( *value*[, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Dict, DictStrAny,| Dict( [*key_trait* = None, *value_trait* = None, | | DictStrBool, | *value* = None, *items* = True, \*\*\ *metadata*] ) | | DictStrFloat, | | | DictStrInt, | | | DictStrList, | | | DictStrLong, | | | DictStrStr | | +------------------+----------------------------------------------------------+ | Directory | Directory( [*value* = '', *auto_set* = False, *entries* =| | | 10, *exists* = False, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Disallow | n/a | +------------------+----------------------------------------------------------+ | n/a | Either( *val1*[, *val2*, ..., *valN*, \*\*\ *metadata*] )| +------------------+----------------------------------------------------------+ | Enum | Enum( *values*[, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Event | Event( [*trait* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Expression | Expression( [*value* = '0', \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | false | n/a | +------------------+----------------------------------------------------------+ | File | File( [*value* = '', *filter* = None, *auto_set* = False,| | | *entries* = 10, *exists* = False, \*\*\ *metadata* ] ) | +------------------+----------------------------------------------------------+ | Font | Font( [\*\ *args*, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Function | Function( [*value* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Generic | Generic( [*value* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | generic_trait | n/a | +------------------+----------------------------------------------------------+ | HTML | HTML( [*value* = '', *minlen* = 0, *maxlen* = sys.maxint,| | | *regex* = '', \*\*\ *metadata* ] ) | +------------------+----------------------------------------------------------+ | Instance | Instance( [*klass* = None, *factory* = None, *args* = | | | None, *kw* = None, *allow_none* = True, *adapt* = None, | | | *module* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | List, ListBool, | List([*trait* = None, *value* = None, *minlen* = 0, | | ListClass, | *maxlen* = sys.maxint, *items* = True, \*\*\ *metadata*])| | ListComplex, | | | ListFloat, | | | ListFunction, | | | ListInstance, | | | ListInt, | | | ListMethod, | | | ListStr, | | | ListThis, | | | ListUnicode | | +------------------+----------------------------------------------------------+ | Method | Method ([\*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Module | Module ( [\*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Password | Password( [*value* = '', *minlen* = 0, *maxlen* = | | | sys.maxint, *regex* = '', \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Property | Property( [*fget* = None, *fset* = None, *fvalidate* = | | | None, *force* = False, *handler* = None, *trait* = None, | | | \*\* \ *metadata*] ) | | | | | | See :ref:`property-traits`, for details. | +------------------+----------------------------------------------------------+ | Python | Python ( [*value* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | PythonValue | PythonValue( [*value* = None, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Range | Range( [*low* = None, *high* = None, *value* = None, | | | *exclude_low* = False, *exclude_high* = False, | | | \*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | ReadOnly | ReadOnly( [*value* = Undefined, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Regex | Regex( [*value* = '', *regex* = '.\*', \*\*\ *metadata*])| +------------------+----------------------------------------------------------+ | RGBColor | RGBColor( [\*\ *args*, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | self | n/a | +------------------+----------------------------------------------------------+ | Set | Set( [*trait* = None, *value* = None, *items* = True, | | | \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | String | String( [*value* = '', *minlen* = 0, *maxlen* = | | | sys.maxint, *regex* = '', \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | This | n/a | +------------------+----------------------------------------------------------+ | ToolbarButton | ToolbarButton( [*label* = '', *image* = None, *style* = | | | 'toolbar', *orientation* = 'vertical', *width_padding* = | | | 2, *height_padding* = 2, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | true | n/a | +------------------+----------------------------------------------------------+ | Tuple | Tuple( [\*\ *traits*, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | Type | Type( [*value* = None, *klass* = None, *allow_none* = | | | True, \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | undefined | n/a | +------------------+----------------------------------------------------------+ | UStr | UStr( [*owner*, *list_name*, *str_name*, *default_value =| | | NoDefaultSpecified, \*\*\ *metadata*]) | +------------------+----------------------------------------------------------+ | UUID [3]_ | UUID( [\*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | ValidatedTuple | ValidatedTuple( [\*\ *traits*, *fvalidate* = None, | | | *fvalidate_info* = '' , \*\*\ *metadata*] ) | +------------------+----------------------------------------------------------+ | WeakRef | WeakRef( [*klass* = 'traits.HasTraits', | | | *allow_none* = False, *adapt* = 'yes', \*\*\ *metadata*])| +------------------+----------------------------------------------------------+ .. index:: This trait, self trait .. _this-and-self: This and self ::::::::::::: A couple of predefined traits that merit special explanation are This and **self**. They are intended for attributes whose values must be of the same class (or a subclass) as the enclosing class. The default value of This is None; the default value of **self** is the object containing the attribute. .. index:: pair: This trait; examples The following is an example of using This:: # this.py --- Example of This predefined trait from traits.api import HasTraits, This class Employee(HasTraits): manager = This This example defines an Employee class, which has a **manager** trait attribute, which accepts only other Employee instances as its value. It might be more intuitive to write the following:: # bad_self_ref.py --- Non-working example with self- referencing # class definition from traits.api import HasTraits, Instance class Employee(HasTraits): manager = Instance(Employee) However, the Employee class is not fully defined at the time that the **manager** attribute is defined. Handling this common design pattern is the main reason for providing the This trait. Note that if a trait attribute is defined using This on one class and is referenced on an instance of a subclass, the This trait verifies values based on the class on which it was defined. For example:: >>> from traits.api import HasTraits, This >>> class Employee(HasTraits): ... manager = This ... >>> class Executive(Employee): ... pass ... >>> fred = Employee() >>> mary = Executive() >>> # The following is OK, because fred's manager can be an >>> # instance of Employee or any subclass. >>> fred.manager = mary >>> # This is also OK, because mary's manager can be an Employee >>> mary.manager = fred .. index:: multiple values, defining trait with .. _list-of-possibl-values: List of Possible Values ::::::::::::::::::::::: You can define a trait whose possible values include disparate types. To do this, use the predefined Enum trait, and pass it a list of all possible values. The values must all be of simple Python data types, such as strings, integers, and floats, but they do not have to be all of the same type. This list of values can be a typical parameter list, an explicit (bracketed) list, or a variable whose type is list. The first item in the list is used as the default value. .. index:: examples; list of values A trait defined in this fashion can accept only values that are contained in the list of permitted values. The default value is the first value specified; it is also a valid value for assignment. :: >>> from traits.api import Enum, HasTraits, Str >>> class InventoryItem(HasTraits): ... name = Str # String value, default is '' ... stock = Enum(None, 0, 1, 2, 3, 'many') ... # Enumerated list, default value is ... #'None' ... >>> hats = InventoryItem() >>> hats.name = 'Stetson' >>> print '%s: %s' % (hats.name, hats.stock) Stetson: None >>> hats.stock = 2 # OK >>> hats.stock = 'many' # OK >>> hats.stock = 4 # Error, value is not in \ >>> # permitted list Traceback (most recent call last): ... traits.trait_errors.TraitError: The 'stock' trait of an InventoryItem instance must be None or 0 or 1 or 2 or 3 or 'many', but a value of 4 was specified. This defines an :py:class:`InventoryItem` class, with two trait attributes, **name**, and **stock**. The name attribute is simply a string. The **stock** attribute has an initial value of None, and can be assigned the values None, 0, 1, 2, 3, and 'many'. The example then creates an instance of the InventoryItem class named **hats**, and assigns values to its attributes. When the list of possible values can change during the lifetime of the object, one can specify **another trait** that holds the list of possible values:: >>> from traits.api import Enum, HasTraits, List >>> class InventoryItem(HasTraits): ... possible_stock_states = List([None, 0, 1, 2, 3, 'many']) ... stock = Enum(0, values="possible_stock_states") ... # Enumerated list, default value is 0. The list of ... # allowed values is whatever possible_stock_states holds ... >>> hats = InventoryItem() >>> hats.stock 0 >>> hats.stock = 2 # OK >>> hats.stock = 4 # TraitError like above Traceback (most recent call last): ... traits.trait_errors.TraitError: The 'stock' trait of an InventoryItem instance must be None or 0 or 1 or 2 or 3 or 'many', but a value of 4 was specified. >>> hats.possible_stock_states.append(4) # Add 4 to list of allowed values >>> hats.stock = 4 # OK .. index:: metadata attributes; on traits .. _trait-metadata: Trait Metadata -------------- Trait objects can contain metadata attributes, which fall into three categories: * Internal attributes, which you can query but not set. * Recognized attributes, which you can set to determine the behavior of the trait. * Arbitrary attributes, which you can use for your own purposes. You can specify values for recognized or arbitrary metadata attributes by passing them as keyword arguments to callable traits. The value of each keyword argument becomes bound to the resulting trait object as the value of an attribute having the same name as the keyword. .. index:: metadata attributes; internal .. _internal-metadata-attributes: Internal Metadata Attributes ```````````````````````````` The following metadata attributes are used internally by the Traits package, and can be queried: .. index:: array metadata attribute, default metadata attribute .. index:: default_kind metadata attribute, delegate; metadata attribute .. index:: inner_traits metadata attribute, parent metadata attribute .. index:: prefix metadata attribute, trait_type metadata attribute .. index:: type metadata attribute * **array**: Indicates whether the trait is an array. * **default**: Returns the default value for the trait, if known; otherwise it returns Undefined. * **default_kind**: Returns a string describing the type of value returned by the default attribute for the trait. The possible values are: * ``value``: The default attribute returns the actual default value. * ``list``: A copy of the list default value. * ``dict``: A copy of the dictionary default value. * ``self``: The default value is the object the trait is bound to; the **default** attribute returns Undefined. * ``factory``: The default value is created by calling a factory; the **default** attribute returns Undefined. * ``method``: The default value is created by calling a method on the object the trait is bound to; the **default** attribute returns Undefined. * **delegate**: The name of the attribute on this object that references the object that this object delegates to. * **inner_traits**: Returns a tuple containing the "inner" traits for the trait. For most traits, this is empty, but for List and Dict traits, it contains the traits that define the items in the list or the keys and values in the dictionary. * **parent**: The trait from which this one is derived. * **prefix**: A prefix or substitution applied to the delegate attribute. See :ref:`deferring-traits` for details. * **trait_type**: Returns the type of the trait, which is typically a handler derived from TraitType. * **type**: One of the following, depending on the nature of the trait: * ``constant`` * ``delegate`` * ``event`` * ``property`` * ``trait`` .. index:: recognized metadata attributes, metadata attributes; recognized .. _recognized-metadata-attributes: Recognized Metadata Attributes `````````````````````````````` The following metadata attributes are not predefined, but are recognized by HasTraits objects: .. index:: desc metadata attribute, editor metadata attribute, TraitValue class .. index:: label; metadata attribute, rich_compare metadata attribute .. index:: trait_value metadata attribute, transient metadata attribute * **desc**: A string describing the intended meaning of the trait. It is used in exception messages and fly-over help in user interface trait editors. * **editor**: Specifies an instance of a subclass of TraitEditor to use when creating a user interface editor for the trait. Refer to the `TraitsUI User Manual `_ for more information on trait editors. * **label**: A string providing a human-readable name for the trait. It is used to label trait attribute values in user interface trait editors. * **rich_compare**: A Boolean indicating whether the basis for considering a trait attribute value to have changed is a "rich" comparison (True, the default), or simple object identity (False). This attribute can be useful in cases where a detailed comparison of two objects is very expensive, or where you do not care if the details of an object change, as long as the same object is used. * **trait_value**: A Boolean indicating whether the trait attribute accepts values that are instances of TraitValue. The default is False. The TraitValue class provides a mechanism for dynamically modifying trait definitions. See the *Traits API Reference* for details on TraitValue. If **trait_value** is True, then setting the trait attribute to TraitValue(), with no arguments, resets the attribute to it original default value. * **transient**: A Boolean indicating that the trait value is not persisted when the object containing it is persisted. The default value for most predefined traits is False (the value will be persisted if its container is). You can set it to True for traits whose values you know you do not want to persist. Do not set it to True on traits where it is set internally to False, as doing so is likely to create unintended consequences. See :ref:`persistence` for more information. Other metadata attributes may be recognized by specific predefined traits. .. index:: metadata attributes; accessing .. _accessing-metadata-attributes: Accessing Metadata Attributes ````````````````````````````` .. index:: pair: examples; metadata attributes Here is an example of setting trait metadata using keyword arguments:: # keywords.py --- Example of trait keywords from traits.api import HasTraits, Str class Person(HasTraits): first_name = Str('', desc='first or personal name', label='First Name') last_name = Str('', desc='last or family name', label='Last Name') In this example, in a user interface editor for a Person object, the labels "First Name" and "Last Name" would be used for entry fields corresponding to the **first_name** and **last_name** trait attributes. If the user interface editor supports rollover tips, then the **first_name** field would display "first or personal name" when the user moves the mouse over it; the last_name field would display "last or family name" when moused over. To get the value of a trait metadata attribute, you can use the trait() method on a HasTraits object to get a reference to a specific trait, and then access the metadata attribute:: # metadata.py --- Example of accessing trait metadata attributes from traits.api import HasTraits, Int, List, Float, \ Instance, Any, TraitType class Foo( HasTraits ): pass class Test( HasTraits ): i = Int(99) lf = List(Float) foo = Instance( Foo, () ) any = Any( [1, 2, 3 ] ) t = Test() print t.trait( 'i' ).default # 99 print t.trait( 'i' ).default_kind # value print t.trait( 'i' ).inner_traits # () print t.trait( 'i' ).is_trait_type( Int ) # True print t.trait( 'i' ).is_trait_type( Float ) # False print t.trait( 'lf' ).default # [] print t.trait( 'lf' ).default_kind # list print t.trait( 'lf' ).inner_traits # (,) print t.trait( 'lf' ).is_trait_type( List ) # True print t.trait( 'lf' ).is_trait_type( TraitType ) # True print t.trait( 'lf' ).is_trait_type( Float ) # False print t.trait( 'lf' ).inner_traits[0].is_trait_type( Float ) # True print t.trait( 'foo' ).default # print t.trait( 'foo' ).default_kind # factory print t.trait( 'foo' ).inner_traits # () print t.trait( 'foo' ).is_trait_type( Instance ) # True print t.trait( 'foo' ).is_trait_type( List ) # False print t.trait( 'any' ).default # [1, 2, 3] print t.trait( 'any' ).default_kind # list print t.trait( 'any' ).inner_traits # () print t.trait( 'any' ).is_trait_type( Any ) # True print t.trait( 'any' ).is_trait_type( List ) # False .. rubric:: Footnotes .. [2] Most callable predefined traits are classes, but a few are functions. The distinction does not make a difference unless you are trying to extend an existing predefined trait. See the *Traits API Reference* for details on particular traits, and see Chapter 5 for details on extending existing traits. .. [3] Available in Python 2.5. traits-4.6.0/docs/source/traits_user_manual/front.rst000066400000000000000000000033651300633736300230550ustar00rootroot00000000000000==================== Traits 4 User Manual ==================== :Authors: David C. Morrill, Janet M. Swisher :Version: Document Version 4 :Copyright: 2005, 2006, 2008 Enthought, Inc. All Rights Reserved. Redistribution and use of this document in source and derived forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source or derived format (for example, Portable Document Format or Hypertext Markup Language) must retain the above copyright notice, this list of conditions and the following disclaimer. * Neither the name of Enthought, Inc., nor the names of contributors may be used to endorse or promote products derived from this document without specific prior written permission. THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. All trademarks and registered trademarks are the property of their respective owners. | Enthought, Inc. | 515 Congress Avenue | Suite 2100 | Austin TX 78701 | 1.512.536.1057 (voice) | 1.512.536.1059 (fax) | http://www.enthought.com | info@enthought.com traits-4.6.0/docs/source/traits_user_manual/images/000077500000000000000000000000001300633736300224315ustar00rootroot00000000000000traits-4.6.0/docs/source/traits_user_manual/images/adaptation.png000066400000000000000000001012541300633736300252660ustar00rootroot00000000000000PNG  IHDR<~ CiCCPICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/9%bKGD pHYs  tIME*4:L IDATxy|Օܪwٲc`=’$ d~ !{ M$L2Yf !yM =llco]jGu$/`chcT]]un}{9BJ)Q( B񮠩.P( B BP(J BP(!V( B BP( % BP(!V( BXP( % BP(+ BXP( bBP(+ BPBP( bBP(J BPB<Hlio[P(c@6 }tuILEHr:{KyCURb&M}!uU( !r έ́tB֧ ];P3Nq1DP([;=ϳ 5zݸ=.viR,)%ec6i˚d3tL:eva7VԖsYQ( ⮾1$9+SP^>7/υ "\&G*!ːgF-Xi ЄsS( m¦Fq[ % #I2!I]U^RYKd1t B15x_zWt@4+VpwliL18P˯e 4e!+ b q4?Ŷ}AK41L$菓Ih7T7W]Q B2X1W1_)jNa.S:t&~}^gUNEO(._>E]} c[9m[ܽ]/{dq5.]u4 4!l[ i٘$kuMI IyJ|O,~8нŌ5>!q7\M*P(N C7ӿ > &\}n|nDžЎ랝3m9T$5'$3Qms^Jv.7{KIctES󱫿PݢP(+?}>7eAe1>mK" dXr2 Q><ϺYK՗4BG?5frGI}_/dqB11:^\OK& ˼Tp":RJb$XX^6P}g+!u[Z4ͬ$PS#=X_3_:EP0xnlMCe^hZH89Sy!/4͋l󄣝TT+S-H$R?&t>zTB8!އ n,衾2HwblR;+ k:neɬ5^1fB~Ɔ0cv5*40|7;h*_P 1c.fՔMiAiCPmax44u+.ifLo/?B0kN-n5/|l w7ٌ3?ƙKRuB8fwʮ}\rCi P[9( sV3/O'\HJG)UA%58.6hb@uB8+b~{>G.9Hyų*']:sʙTq*!~;6n};p *Cj'>2?9+cKuBxKr[<!4KP˘%WKn5W F{[x>%Gw%dxj=6MY'Bum)m߳tRP(Hz7~^2X8ڊeho*fǾG'jW|DzC U:`z{U(H獟 pAS+xp; A6 mJ V޸5JN4McoǫtUP(nc˝S0CצŹ̛Q 6?¶{l3`ˮ爧z] X(-w7=:DPmc=_ĩ.3l/X0a0yv&&c*+!غYA! 4'_݆Q2.Y|n31t?Oo!COx/4cftJB>ټ! 4&毾H6GYi'μrtMh{W4r}:/J7m\uB1]6;h/S<r8b[l#e~uU1å3=HO~! 4{}3Bs?PZgZtB*5`v!<ۥ1F%U;:D'hpcTJߵڿ'h42Rե^l뿁m&w>(kx :NVu~z.\Ȃ Ng><ӣ0_2^WX<<ꪫp/344?1^nmtM~֭[ƍOKFٵv$TV[\SJHee%k׮糟-Z38-?۶m\}Փj{d4LYc.` M~;&gf㲈S8=hB+x!n4;t);旿%eee|ӟ{aΝx,X^nϒ%K1 L&smFEE@dYNoΪUk1c3fvSUUg]<. ˊ3gg}6fbܹ;6l HOxb㴶rwzj~_[RM6p #@:?!vf~a馛(-mv?1VEH1hs/ΞҏJvr\Gٳϣ"NU ޽]ubLX~=_Wꪫظq#gqPիWs֭[Yj_җxgyя~D2[og޼yz-Ͻ=Ek,c͚5\|{Ok.VXQ<믿˗pBnʆ /|/袋hjj+V.2.RcyOg>JJJG?:.ߍmZ[[y>(ՇHto6/ 8B0ƩwO?&N?.t~G$< יOӊ1|֜go]t:כ˲x{^ۙ;w. .䳟,vXիWH$k4axks7r嗳h"l??WYY~~rr޶ݱXGۇbHTϑ7'B%H+J|nJ|nb!=O͒ #^ǂie̓odi_ckxnۏF S) 6bZ{J8l?efjJ[4݃[mjJ -Ŷ>O9eElibhfV@+=4T,"g929+M[̪ds _'Bb !_= ݃[F.u@{hXBW@lgyh: '-fkGr 熑JR|&Lǩ:p*doNEPUK|RYz^{hj q6~'c2aNs#]9o&c(сU²WOvθ3It#,y /ﺇ}/3܆W:#H{*XzAo KqJlx[?@"=@WoR3}Oqd^مX{ynۏ9ny ?fV,j-mxݥ,hZexYдxK!.$zRjd)u~Yӑ-O:Rc*^ڴ(Ѷ d"xJk[X'X$D4JDS,i 6o^7_ of `3 fd.9]Ϭ^cM,s8}hNB\oY==NM%W.}Y)\ |-VwMvu>S[ )%g.d{{k˾L$K;Zș8B82[B s&(IҿyW]5,%t,kCw RQ?m4V.b)Bh^l?Kodq+u_`'?$ɟ`F2TsVV*Kf!FXOHok[AIQ;Άӓ|;455M9}G}A$%ϞBvV;WU+.mN~dQYz {{+ A~ h )m2kMcAlĉT̄OxRJ\ >vVɆ X~=L+;p5}466J޲}2d466rJW_}~P(D[[_XIwŋۿ[ZZZ&lI"v _ݧǓC$%־ujXuB\W)!ୠ*t(z9Itp0峯!}MrشwHiZҹ[<@l/jW2iU:ǢJgxv_GJ,ުb@ױ Qj*Ԋ6璶MG57||#%\͛Bp%p]wDWOӣr|SK_7|3v.EQ[[_8՞/_-׿ulg_[榞$zcR<. ]UOt7&qZo6uֽ^x3m̆yqݴ2햶Є΢KY:Jlۦ?wNf¼z-k@8ήjh%,y)}={E<X6r;ykCh`+8fO~7KoK23C~~4M+6bbr 7EOO3f̠sspmؑWUU1sLp-p5HSS .,V{z?Sm}svwǩRMH\{Ҷtqq%k+==hK~ `Kv|e>wG孶ŋ;~{5|n-y,3Br ;أ>6݇[(9K 7́|=2Co7#ĥ%UjD)&$'x<,YhhtT-]׋+Fg d2m=Igɞܪxi ͒W8}?YHI2=R٣ie0|h4vth<#}1E2{KjD)&hӃiEsKK ml6s=GKKQݻݻwO?Ͳeˊ=44QI 8I|IdMΩ!5G6QAeZ"Wi# q饗reQRRB0đXKzjimm-~>R~s#:{/y{Kyo~sjOG4Ivqوy{0S2z&1W6ȤMJ6+5\W٢:C1a뮻bQU~L&b0Ծ xb v(Zsa՞I#١@q=5mQ*B\_uZIfh)q} M!JYGk_HՑ4YDer)4c+_0DvlBT2S"nCXL&1xyș?L _$;p\v-mX>1o͘V xEl7jfD%u bˁ /Ck{_ WzS09z_͘ZuB,r./; HC iSS>F! 8ݘC$ىճ LY a#@0;7B\1[r?[{xdhKGF̥іmŇ@Zm!;^tR]1­%5ta!XRG&6z~^Qb[6Vnl1Y{Lb-HG_]鸨V@p-ۑ#ԉ^=O N1ĂKayd܅53dg,Kr}1e1$s.?(RJ!;;8HO'FB#FCb yV~ ]fnŞx̄!3Iδqgv-[: B^)n!_;|ѩ2׷ 1D09΍ %t2{^!5o5l{̚y!VL=!^ز?\M4G"$ `6A-}TP&RiEGG72wXx55 B."%ѱ8%Q-a:R0MsӞF2CdY~Qxe HGe>w` d}eN|bkby%Nc,vR]1_ >@sKJq vE,pC8.+B0 Rޙ0s`/6.#GPh'˙[݊UՄu-XK)0vdfل0 $IdzU"bW<-bcvtװU5Dm} eM2k.{xo`A0'N&RCCr^ f ͙[6g=qՄ%눅@YDVyqSa3fo' dFDh,{0P]z:pG!Z:#I9ovsX{1= ٵ:">cA4X e5GN\%}q95iK$H%Ԕso3N<Ǥr 6T:VLY[U!Ayb!H }e>Ga8?F ƋEkq]m~a)!/4}Q%cM,#37Q8 1D,f njSjmH q_e6)%\'t)ul[qV/˭X1e1 `{B8N_wz>.yb #‰`ަɯ)a,%uމ juM 6@߯Ies*hk rמ69 )g%[6};\4ͮFIIOG\V2cv.)s>z.d)%e¶;lEJ [W )5*pyJSol僤6z{zygE-,:65&l<"Kd0х!r%nyɋZ9z#aO _#LJ`~(ڠjlNop&~4ܸ 1@2@ESs1Iw{x,MQ7 beG)oq\! m/cбFQfR(Wb~7l. ac`̢yv煷j:6`R!{qG0f en^o r ǐVų4މg0AK{E\ pufUrM.[J:R2Bca{tۮO< زQu>Zm)yز5$zQF^KuY RL;ҩ\[1eqְhEA܆3^%Vˀ! R:\.wQl}>_gۍ0 4MCu>=z^SU|"LM{O`] $ͣ@ Caf>{jeG"J[/[Ɗޅl6{^ƶ]ض@-aAF m Mv{iX1ػ};#FAdхU\Z.ZdIt7@x<[|G >% =k RZ,= n}S1|\5 7/A`;ѡ&=SbH&Ҵ 1 yқIdɣ⎧x` ^wbl6xeH)ma-aulc˖CSBUJӊRn݂mhH"yՄD0M+l^hSʛWRQQAEEeee Ea.q5R _l"Fs+ٜ2U_; $sp'OR"/^в i tI"! zSb}z:jJ-M+ypit-TS;z Μ@˸2ʂ '"9w˶R4,XÎE[g[]@j4V+1VL4]TtVa#B M8e# >taaC^A6lZ@eeeQx ~c\ߣ>$4Σ絇H&}nj!5]qu]2 qATf5.f{_#LJ=;t?5<0@t(@paݹexy$ Y {b.m RS6XJI4LJ)9.i(ѕH[s mA\:{%̪i:@1b240@o/NXA Z [k= 0{$mo./O-Ux΁kry[O(/cќG8M," 'zc[D$3) Vs_gќsQ@8I*ep iڸ\:ƻ2mqmIe .9Y|o;M]0(E&+ ZvK[͎bH.B^\9b;G{x~ؒah$\YI_O)ݝ|sK1M5]9f-c!SB8eOЌ:vo#5*ʻN*c3D2}Hi=x9ؽ6=ۼ>R?%!I+"aKD$E4" UKdAwӶm,"N 3`H(ѭJ,W ii #IVrq>+̨/qd6[ YԟNYI}>R uql\Eċ?QDNH$clQ|5X` ' PHQd Hm! 9|oa]Lء+[莖 ",K,(fe!ક9cncŤGJϼޝ{S[2gXx4gDsPktbgOg oͻ>mQ]Belk$j^&>WLh!~5m&M˥q{\utc=!iKEjf-rE6cNfr Mn\–̝u:>OpoRJrDh4J?MyײiBj ym;*R.X qaZk{ؒ>cVWZݥ Ŝ#]aȢa g;^dǽ_AJR*CiPd\0eR.]xmy@P_5 ٰY q|'L$Jѻ%ٴ;"[VA`v;[H};,y74awm~޷rx<e+&Սڲ,$/7vt,]b%G"]S$"/:.2 5qy_B)Җb([~wIŔ8IW}A4T71i3 H'C>">tNn``eJk.AUy~W0 ,KZ愁`FKX]#DwD;gْkIRpG۶F왪JbZ7lS_BK|mb SjhPXǍG `I CXX6!Ε<@:>S7nTbmO]MImZ}ZA,. [SF'&bBʲI%xqHQw8Qp$t!a,#E>(KXФwuk.,TWni'hn2ՋDbJ!z>2Z1"/6BXy!  zaנDڴ:4+mQLۦrzpLHfrxgC_EԺfհ}E8֟EQEȼN[1"BzXs2ևoܗ#.qތj*~X)FlVaz D~ ) Y$8S@ )#!XAo+e1޳| gM3菳;m)]&2&[ gɍߛr"xeYH/3GejK=ÃF,lRcuS 5 ys*r?&NЦeሱ jVwgtGl1<0+6a&'#ܦXWp$y@T֜c`ma29Ь,ȿDORi߃t nUx]rm9n7^?6jKotr>$lGCE IU=5iM:i~Zf^ɶx*pwxc,[F'ݰG'TlZj{Dʼ57לյo}g۳`0Hyy9>Og))144ğ_A<8,< . a9i. ٴ4 i}za·[GSUͱ ^$HJ)ib1v9sUÉ;Wv%>gP-mjkVnŞ ޱS_̏6,T֢k Fԩ.Nê0=Dwݵ$>Ǔ<$s#FEHQˎ F'-=^ IDAT,KS7L|XAz -;8)k}aߗqfFVYT6 7$^cb/1"ɽnyI$DQ#zA $( 63=3TGwݳ(p|9y44M~31MJ?KlFVT7n:,*TRFM%{d^v*sIu]SN(Gh4Xn &*Ն`S ;cڬ)3EUA[%&r6)N3⍉oߌ7Qf9V㴓$=ɍqn W:HEMa=biv7Ϡӈ˵*y2`kњTU?:8>B0"Uh`ajvu.8B>_Q0 ˺Ƅ8&V!Am!%W[;l);In^ʼn:u t SwY^$BB *:ā:bǥu{\`f-GY wĚYk|lAh۫:k}qs-|0 C;(|={7R_q"niWqm5{L4 PȠ>>F}z:^0(w3|>(++cr2h v#1qYbB5u0#-caBpZ_nn{*ʎ+EuNjPw|/i gNrHv;1 #7zZq kRol8NM)'2b'2dC2ЂoaAn>3;L+-8UńjЃ hvT́IAs<*}&E""tZ" C8\2hD|QDtt LjJDBai 4=ͱR5ΨB(.H;9PD=W? w$zMbӀiْ@,'ntՙ\+؊VR[qlBu)_}pXYfvn7>Oi"nb( nۚCVUfgGA|a=!i%1rY:fgp8ԕ({ڗ՟6jI}!nNH_ZRHy0VTKtMS;PPPCvIIJ1"‚ Opg8bG5T*Fn F.!hb*.233bpwzՄJ%L&*Cy! eR >fq\c뺸"n1XkRi8g6:YԆ2 ENLj%lm/B,iŸKZ8ʶ q8r!n(E) uqιscJqq1?O$r%UYfB,eQmP5: (i aF"01=ܢ^Ep}1yd{Ǹq 37n'Nȑ#L4ÇG!K{:u*[owm3|ݻk" q*ƯS%Q}َjTEDf-XatTMG‘0OLL|-]-#}ꫯfǎݻ#G Xb_طozz/A'pwnYA{9o뤦3x`k>K,a۶m͛9tW楗^SNk׎˗/R__O(K/k׮lٲLBVVn:z)֯_O.]̴}ҥɦMꪫXlCYYуe˖~;6mDAA@_lذJzA0䩧^ȑ# 4UUygᣏ>nn݊gԨQA~iy|>2-m 41z>8!/^'x-Z] bbtP UDQLՌXZE3A1P40(Io6klذロ"ϟo77 7x#]ta޽l߾-[XYd ~ϟ~i(**bɒ%Y?Ou]$'']v1{lڷoϸqk0 K/t&MĠA=5kְxb̙e]Ƅ (+k<sND<f"99]vyf{wk2q<ǏK.L>vڱk.F}ǺuXjדO> 7܀f;lt,?l̟?_^zIZ4+ ᵏ^u;?~{_EA1ma"֭E\jO3⫣8v6E30=^XΚ &2u`-Imm-@+VP^^f{!55Zq]w1tP MgYp!'NdΜ9 >p8ҥKx饗9r$O>G?"^;6mZ%iTUUѻwo^/$##پy~g[3fp-PXXH^3gC=d[C~_ѯ_?v;-B4rssIKKʫ9t}?0#77ʕ+B˖-ȑ#ffϞ-w2Ahe `U`ۍy|;(15Mi2U@1Wa%V5Pbm3ĚIdcOi˘ڼ׏jӱ"lHKK# RRRBnS٧,ÑXp@yy9>"KzϨdaYYT׏< YHԭp8My7Xt)ӦM&4?vݺu/ 7TY8)FR4Ƚ++bmDE$4&-9-T#l n>+:i!/|MkuϞ=sNڷoOZZ '222πHÇϨ222,0S>9N|>I-₂֮] x;v<+%Rʕ+Yp!FKxHٸq#yyy|g?GFΝ1b={BV ¦IxoQK>TM{Z1 ՌH[AVQFF7jkG.E?b.eL `XʪYQSЪۜ'%%1bTUcǎ \tEX뮻^zQZZʲe?>]wo&>e˖1ay*zɠA 9sf;v'hr3g2ydϟo͵6\`nf}]˹[MLxc߽{wB_~9O<g?! @4hmcƍygl2fΜE]Dee%~;ӧO; p!Bz,fD Y1EU^M8 4,O&舷xuoDٓi'\[Π?0eLy֣&99>ǃa[z][[ Ixlj_555VZxKp7Mj+)G Rmx ׼ -0wx+_ 86#j)J-6<궎2J4 Lyb4N4׿jXLAI4Mfww&5Y9 7"l6cjuK+8wsLx-K[Yݑu1(o!AZO_-ѹ\%dSk#!%ꖵjZ.Xh8n)${;(`D&հئ>BǴ8"Ă BBlURJŬF8'c)VP''E5"P佘jIg5plVUnJ!B,HOOhnD*hb%fCv"e 獸k׺ciD=+C<QZ6[n`QX a3?%(tvm,B-c-Zk&F1 m+E PD2vtMa\vto$,h!v:TsqkMl h w]r΃W?:x.:ȏ&JMvB<̠XK+h+:@l7gQ+7vtA+0n@яT x‘hPP뙃8 8V\ `&^{-O>)ۿ?g֬Y8&#_L~~|1Au]gOKcl=R~rJ"-*lԪ#kzp$(KF8ką EymIHPNIwb(ہBqEzlb쁨ZJNZc;zpX V_Jee%6ŋ7>C=D~s5tjO͓Qdњ3>-f\l<.'NZ[>Ink)k5{:>GR5ޑ}8z@Y<C*MD@ia0Ѡ-5/t=UU[oH,soYX~=AmmmBMw AՍbW{*))IT:O܈p8f󾲯}0~\b-NczǀXtk~727Q^mx4_AZ02/G7ujbMU+JIIt_һwo6lp16l@^^[l5u= /g"E_FUp8|O b Ekh˖NTURC+vi5}=;z`f *?n:v ֲ& L+GR鿘>dyZ߃9jEK,aҤI1tP+Ծ?x5t՞3@HzfFR3]UŎ&jԡ[LZ?ߏV19~8:tcpb Jtpzވ+j(n Q7učxǞ3 \``(*Uc(BeMjzpD *@>SV1 M:u*=]vMnWGSW{N0Wh7&׎fktywذawG}h",YgŊ$J:JCK4jXF3 6M#>!8O ^EMyP*j^v;vӉrrp8VR,sLp?Y6~ScSRRR(Eid}8X_>rB&o:o999 6e˖vZ?4Gjj*7p)*((>,p(v:Љdj *z4WtlSTnp :>jNLprp8):iCikpVXAjj ķd{|ppD }eԨQу?4ZW>i$>Sƌ͛ Bر{2g9x ?0cǎꫯ_fƍdff2o<:u@}}=K,p w 7ᯋ󑑑~%\OSv͟gݺu뮻x;#[Ul읩_˹0w)*| vR"U(u17φ !6j4[ˆ}fΜɓ?>ӦM׿5dddzYYY\veAfΜINN~[.!l\.k'\yD3{$9eEv# -ፏd 4Al2XU"qLO׏mT5~}zj\u4MkY555Z.kB!t]v7#FB!S]]MYYr؟fKXVPt " \qsMÛ`L ~PײjY ׼78D[rHOOXcKҲ"bAi^7a~T B, !i""Ă ·!"gI$yɺڂ B,p;w.{쑁bANݻke "sĂFW_sάZaÆq7( ˖-cX[n}0w\t]=[nTWIDATeѢE̙3=z[or馛۷/cέJ>}8z(_(+++`ʔ)XĂpa;ظq#W^W_`֬Y$''swSTT~x<ƏO.]>}:ڵc͚5,^9spe1a(//ꫯfС\{l޼j&Laø~k&_ E,ݺuc…̟?UVqC1tP+V=Cjj*@\((('??|Oκu4QF1c oW^ɕW^ ~3}YquP~_H'xQ̜n&ۛɛoɚ5kHMMn{^7t:ٳ'Ǐ} ۿ;|V>tA8gzդ[Ul6iii*'jNMM жvԖBcŜƍcĉ9r'&lW_}Ŷmۘ3g.SZddd0vX|>FbӦM ?Xsٷo}aڵeOL,CNN;w}QRRBiiնk2x` .7x#,[ i#//?v>ƌ#_J ^z={j*&LСCOrr2 ,P\\̊+8x dܹl6>O?'%%#F0qD>̲e˨g 4Ⱥy˨Qh*sΖG,\@ 鴬1c0frss;زe ۶mo߾\wu̟?͛70`ݻwOx=c ^~e6nHff&#=='xEQbʕ>""--{dza-o xŊ\wu̘1,[ ݻ ٺu+ ,waҤI\zzr%PPP3f ֭cС\y\r%L<۷3o޼F - Y~=7x#v?6mEEE,Y]v1rH>֭[ǪUH3f|gy/jf̘iӘ1c_=CslݺѣG["ܐcDzsN*++xW9s&ӧOgڵٳgsu{w^zիٺu+q?~\,bAU| _!;v,۶md}M_z5~۝UU/K~pB5 -ݻ[ĉ3gÇ'tR VСCӇ`hadggzjz,͛73y&o.!PSS~3|fAQRRRa^|E><۷3`͛^̚5өS'v}^}U xyE\*kV!34M!&JM£hꙧ TFǔƋp"mxnxC'6ܐxa۱տ2-x?7`ҥL6BrrrPUA1ev;?QUG n7s`sr]w1bĈfqq٬]BK\:+b q>uQQgfԩ8NMƐ!C;9x  !(v&3G(b~Z&uSꙻhUUvP!YYY{E 焕+WpBFc=ixgIIIsQ]]_oվ>"wѣ曛ݾ{wŋ( //Ì=_<?K.޽?ϖiܸq}֔ e4 Izz:. 4(0 Pn?#XUUv;⣖TU캉cՖ_̞= 0M8pe.]z6mbYUUٽ{7s!))3Ϝ5sk( |֭#  Yf @$fq|ٳg3k,ڷoI,bAXFh4Wh޼y̘1tܙC(I+θq }0sL&O;j^F(՗\iѷRZgg۳#CrM m*xo+(..殻0 \.WB555x<kMUyjXĂ BE'kp8#)))QؚREX,bM$ Zz6sus:d5#QWWag,XB0 ?@[3wwv gWE=d@S,!Pp"B,#TdUW RAbAA!AAXADAA!AbAADAAXAbAA!AAXADAA!AbAADAAXAbAA!AAXADAA!AbAA@-&G+_R{Zێ&c_}bbAV}%5~C{=ˋa>2gsA! wNK(lRrhx6wI;dlBl>Pi-l MU\RXiט6 ϯ=%|| B,\ (|/{sU}'7@UμqST^  t<7./^yo,+Gv~f B,\|qG˦2*'{%sOG+딌ۡ`X Cz)ŗl?5w:^d]зak_aL7#Sl>PAU]%85A Ӥ.Jڧ91 /p,kr#nʪl=\ed$9p94>_܎I|!s26Me*ֱ֞ B,\uº@f{YG1a@{SȌО$ M4##+L,qg/lgimwqTzQ(_zzeyXx}?g"sIW(f~E_e ;Яk*oou_3GvnS?/l6k,nGw?_1dNۋ#nT&ǧWIC:Gy^\v팎E3C/ -.̹; /gC mVm:bt yܢZV?ayn_ӡc{qa _ZKYui.4U!cxe~W5z[{Xl*ߛf]3T`H .6P~:z K ao&P#W%2P_Y9g b m]%cWo*J FBӌdEԭ>A=8:N[z #b{r8RZp&QUxd'9*ӆwa'5Ppԓ\;^;{g$Ag٩N*A?;c "BV[j ۏsUAxnһkv86)cNq)|wdޕyia d€¼#XA!Z& ?7b6vgm6|]|oZ]Sr;yWgV]릹~uӴT7LAj:Titpo{5  yo1; . >Ql*\?;7 @m ?+:lW1:n_tJj,}g/?ϼi _4MSA8[u?]t=% Sdrj*Z (bh'^UV 5*a¤1+w=%=S/Kرk*5NvI Lpl GUh4͍ D/Ie3:;CTЭޯňilN H_l{4E7lv*ఫ G_F2rr-ָ==ΧlnLO!2O,"ĂТYu|o7 M6†2@  ¹$}mvL}ӲA!,Q9Z!klAhIq) "Ă  B,     B, "Ă     "Ă  B,     B, "Ă   B&C LLz!  BQbJlRrx p>Tcr}DC8m=r.L4MSAA$XKADAAXAbAA!AAXADAA!A >+g([IENDB`traits-4.6.0/docs/source/traits_user_manual/index.rst000066400000000000000000000004521300633736300230260ustar00rootroot00000000000000Traits 4 User Manual ==================== .. toctree:: :maxdepth: 3 front.rst intro.rst defining.rst notification.rst deferring.rst custom.rst advanced.rst testing.rst debugging.rst Indices and tables ================== * :ref:`genindex` * :ref:`search` traits-4.6.0/docs/source/traits_user_manual/intro.rst000066400000000000000000000162121300633736300230530ustar00rootroot00000000000000============ Introduction ============ The Traits package for the Python language allows Python programmers to use a special kind of type definition called a trait. This document introduces the concepts behind, and usage of, the Traits package. For more information on the Traits package, refer to the `Traits GitHub repository `_. Additional documentation for the Traits package is available, including: * *Traits API Reference* * `TraitsUI User Manual `_ * Traits Technical Notes What Are Traits? ---------------- A trait is a type definition that can be used for normal Python object attributes, giving the attributes some additional characteristics: .. index:: initialization * **Initialization**: A trait has a *default value*, which is automatically set as the initial value of an attribute, before its first use in a program. .. index:: validation * **Validation**: A trait attribute is *explicitly typed*. The type of a trait-based attribute is evident in the code, and only values that meet a programmer-specified set of criteria (i.e., the trait definition) can be assigned to that attribute. Note that the default value need not meet the criteria defined for assignment of values. Traits 4.0 also supports defining and using abstract interfaces, as well as adapters between interfaces. .. index:: deferral * **Deferral**: The value of a trait attribute can be contained either in the defining object or in another object that is *deferred to* by the trait. .. index:: notification * **Notification**: Setting the value of a trait attribute can *notify* other parts of the program that the value has changed. .. index:: visualization * **Visualization**: User interfaces that allow a user to *interactively modify* the values of trait attributes can be automatically constructed using the traits' definitions. This feature requires that a supported GUI toolkit be installed. However, if this feature is not used, the Traits package does not otherwise require GUI support. For details on the visualization features of Traits, see the `TraitsUI User Manual `_. A class can freely mix trait-based attributes with normal Python attributes, or can opt to allow the use of only a fixed or open set of trait attributes within the class. Trait attributes defined by a class are automatically inherited by any subclass derived from the class. The following example [1]_ illustrates each of the features of the Traits package. These features are elaborated in the rest of this guide. .. index:: examples; Traits features :: # all_traits_features.py --- Shows primary features of the Traits # package from traits.api import Delegate, HasTraits, Instance,\ Int, Str class Parent ( HasTraits ): # INITIALIZATION: last_name' is initialized to '': last_name = Str( '' ) class Child ( HasTraits ): age = Int # VALIDATION: 'father' must be a Parent instance: father = Instance( Parent ) # DELEGATION: 'last_name' is delegated to father's 'last_name': last_name = Delegate( 'father' ) # NOTIFICATION: This method is called when 'age' changes: def _age_changed ( self, old, new ): print 'Age changed from %s to %s ' % ( old, new ) # Set up the example: joe = Parent() joe.last_name = 'Johnson' moe = Child() moe.father = joe # DELEGATION in action: print "Moe's last name is %s " % moe.last_name # Result: # Moe's last name is Johnson # NOTIFICATION in action moe.age = 10 # Result: # Age changed from 0 to 10 # VISUALIZATION: Displays a UI for editing moe's attributes # (if a supported GUI toolkit is installed) moe.configure_traits() Background ---------- Python does not require the data type of variables to be declared. As any experienced Python programmer knows, this flexibility has both good and bad points. The Traits package was developed to address some of the problems caused by not having declared variable types, in those cases where problems might arise. In particular, the motivation for Traits came as a direct result of work done on Chaco, an open source scientific plotting package. .. index:: Chaco Chaco provides a set of high-level plotting objects, each of which has a number of user-settable attributes, such as line color, text font, relative location, and so on. To make the objects easy for scientists and engineers to use, the attributes attempt to accept a wide variety and style of values. For example, a color-related attribute of a Chaco object might accept any of the following as legal values for the color red: * 'red' * 0xFF0000 * ( 1.0, 0.0, 0.0, 1.0 ) Thus, the user might write:: plotitem.color = 'red' In a predecessor to Chaco, providing such flexibility came at a cost: * When the value of an attribute was used by an object internally (for example, setting the correct pen color when drawing a plot line), the object would often have to map the user-supplied value to a suitable internal representation, a potentially expensive operation in some cases. * If the user supplied a value outside the realm accepted by the object internally, it often caused disastrous or mysterious program behavior. This behavior was often difficult to track down because the cause and effect were usually widely separated in terms of the logic flow of the program. So, one of the main goals of the Traits package is to provide a form of type checking that: * Allows for flexibility in the set of values an attribute can have, such as allowing 'red', 0xFF0000 and ( 1.0, 0.0, 0.0, 1.0 ) as equivalent ways of expressing the color red. * Catches illegal value assignments at the point of error, and provides a meaningful and useful explanation of the error and the set of allowable values. * Eliminates the need for an object's implementation to map user-supplied attribute values into a separate internal representation. In the process of meeting these design goals, the Traits package evolved into a useful component in its own right, satisfying all of the above requirements and introducing several additional, powerful features of its own. In projects where the Traits package has been used, it has proven valuable for enhancing programmers' ability to understand code, during both concurrent development and maintenance. The Traits 4.0 package works with version 2.7 and later of Python, and is similar in some ways to the Python property language feature. Standard Python properties provide the similar capabilities to the Traits package, but with more work on the part of the programmer. .. rubric:: Footnotes .. [1] All code examples in this guide that include a file name are also available as examples in the tutorials/doc_examples/examples subdirectory of the Traits docs directory. You can run them individually, or view them in a tutorial program by running: python /traits/tutor/tutor.py /docs/tutorials/doc_examples traits-4.6.0/docs/source/traits_user_manual/notification.rst000066400000000000000000000714531300633736300244160ustar00rootroot00000000000000 ================== Trait Notification ================== When the value of an attribute changes, other parts of the program might need to be notified that the change has occurred. The Traits package makes this possible for trait attributes. This functionality lets you write programs using the same, powerful event-driven model that is used in writing user interfaces and for other problem domains. Requesting trait attribute change notifications can be done in several ways: .. index:: notification; strategies * Dynamically, by calling on_trait_change() or on_trait_event() to establish (or remove) change notification handlers. * Statically, by decorating methods on the class with the @on_trait_change decorator to indicate that they handle notification for specified attributes. * Statically, by using a special naming convention for methods on the class to indicate that they handle notifications for specific trait attributes. .. index:: notification; dynamic .. _dynamic-notification: Dynamic Notification -------------------- Dynamic notification is useful in cases where a notification handler cannot be defined on the class (or a subclass) whose trait attribute changes are to be monitored, or if you want to monitor changes on certain instances of a class, but not all of them. To use dynamic notification, you define a handler method or function, and then invoke the on_trait_change() or on_trait_event() method to register that handler with the object being monitored. Multiple handlers can be defined for the same object, or even for the same trait attribute on the same object. The handler registration methods have the following signatures: .. index:: on_trait_change; method .. method:: on_trait_change(handler[, name=None, remove=False, dispatch='same']) .. index:: on_trait_event(); method .. method:: on_trait_event(handler[, name=None, remove=False, dispatch='same']) In these signatures: * *handler*: Specifies the function or bound method to be called whenever the trait attributes specified by the *name* parameter are modified. * *name*: Specifies trait attributes whose changes trigger the handler being called. If this parameter is omitted or is None, the handler is called whenever *any* trait attribute of the object is modified. The syntax supported by this parameter is discussed in :ref:`the-name-parameter`. * *remove*: If True (or non-zero), then handler will no longer be called when the specified trait attributes are modified. In other words, it causes the handler to be "unhooked". * *dispatch*: String indicating the thread on which notifications must be run. In most cases, it can be omitted. See the *Traits API Reference* for details on non-default values. .. index:: examples; dynamic notification .. _example-of-a-dynamic-notification-handler: Example of a Dynamic Notification Handler ````````````````````````````````````````` Setting up a dynamic trait attribute change notification handler is illustrated in the following example:: # dynamic_notification.py --- Example of dynamic notification from traits.api import Float, HasTraits, Instance class Part (HasTraits): cost = Float(0.0) class Widget (HasTraits): part1 = Instance(Part) part2 = Instance(Part) cost = Float(0.0) def __init__(self): self.part1 = Part() self.part2 = Part() self.part1.on_trait_change(self.update_cost, 'cost') self.part2.on_trait_change(self.update_cost, 'cost') def update_cost(self): self.cost = self.part1.cost + self.part2.cost # Example: w = Widget() w.part1.cost = 2.25 w.part2.cost = 5.31 print w.cost # Result: 7.56 In this example, the Widget constructor sets up a dynamic trait attribute change notification so that its update_cost() method is called whenever the **cost** attribute of either its **part1** or **part2** attribute is modified. This method then updates the cost attribute of the widget object. .. index:: name parameter; on_trait_change() .. _the-name-parameter: The *name* Parameter ```````````````````` The *name* parameter of on_trait_change() and on_trait_event() provides significant flexibility in specifying the name or names of one or more trait attributes that the handler applies to. It supports syntax for specifying names of trait attributes not just directly on the current object, but also on sub-objects referenced by the current object. The *name* parameter can take any of the following values: * Omitted, None, or 'anytrait': The handler applies to any trait attribute on the object. * A name or list of names: The handler applies to each trait attribute on the object with the specified names. * An "extended" name or list of extended names: The handler applies to each trait attribute that matches the specified extended names. .. index:: pair: extended trait names; syntax .. _syntax: Syntax :::::: Extended names use the following syntax: .. productionList:: xname: xname2['.'xname2]* xname2: ( xname3 | '['xname3[','xname3]*']' ) ['*'] xname3: xname | ['+'|'-'][name] | name['?' | ('+'|'-')[name]] A *name* is any valid Python attribute name. .. index:: pair: extended trait names; semantics .. _semantics: Semantics ::::::::: .. _semantics-of-extended-name-notation-table: .. rubric:: Semantics of extended name notation +------------------------------+----------------------------------------------+ | Pattern | Meaning | +==============================+==============================================+ |*item1*\ .\ *item2* |A trait named item1 contains an object (or | | |objects, if *item1* is a list or dictionary), | | |with a trait named *item2*. Changes to either | | |*item1* or *item2* trigger a notification. | +------------------------------+----------------------------------------------+ |*item1*\ :*item2* |A trait named **item1** contains an object (or| | |objects, if *item1* is a list or dictionary), | | |with a trait named *item2*. Changes to *item2*| | |trigger a notification, while changes to | | |*item1* do not (i.e., the ':' indicates that | | |changes to the link object are not reported. | +------------------------------+----------------------------------------------+ |[*item1*, *item2*, ..., |A list that matches any of the specified | |*itemN*] |items. Note that at the topmost level, the | | |surrounding square brackets are optional. | +------------------------------+----------------------------------------------+ |*item*\ [] |A trait named *item* is a list. Changes to | | |*item* or to its members triggers a | | |notification. | +------------------------------+----------------------------------------------+ |*name*? |If the current object does not have an | | |attribute called *name*, the reference can be | | |ignored. If the '?' character is omitted, the | | |current object must have a trait called | | |*name*; otherwise, an exception is raised. | +------------------------------+----------------------------------------------+ |*prefix*\ + |Matches any trait attribute on the object | | |whose name begins with *prefix*. | +------------------------------+----------------------------------------------+ |+\ *metadata_name* |Matches any trait on the object that has a | | |metadata attribute called *metadata_name*. | +------------------------------+----------------------------------------------+ |-*metadata_name* |Matches any trait on the current object that | | |does *not* have a metadata attribute called | | |*metadata_name*. | +------------------------------+----------------------------------------------+ |*prefix*\ +\ *metadata_name* |Matches any trait on the object whose name | | |begins with *prefix* and that has a metadata | | |attribute called *metadata_name*. | +------------------------------+----------------------------------------------+ |*prefix*\ -*metadata_name* |Matches any trait on the object whose name | | |begins with *prefix* and that does *not* have | | |a metadata attribute called *metadata_name*. | +------------------------------+----------------------------------------------+ |``+`` |Matches all traits on the object. | +------------------------------+----------------------------------------------+ |*pattern*\ * |Matches object graphs where *pattern* occurs | | |one or more times. This option is useful for | | |setting up listeners on recursive data | | |structures like trees or linked lists. | +------------------------------+----------------------------------------------+ .. index:: extended trait names; examples .. _examples-of-extended-name-notation-table: .. rubric:: Examples of extended name notation +--------------------------+--------------------------------------------------+ |Example | Meaning | +==========================+==================================================+ |``'foo, bar, baz'`` |Matches *object*.\ **foo**, *object*.\ **bar**, | | |and *object*.\ **baz**. | +--------------------------+--------------------------------------------------+ |``['foo', 'bar', 'baz']`` |Equivalent to ``'foo, bar, baz'``, but may be | | |useful in cases where the individual items are | | |computed. | +--------------------------+--------------------------------------------------+ |``'foo.bar.baz'`` |Matches *object*.\ **foo.bar.baz** | +--------------------------+--------------------------------------------------+ |``'foo.[bar,baz]'`` |Matches *object*.\ **foo.bar** and | | |*object*.\ **foo.baz** | +--------------------------+--------------------------------------------------+ |``'foo[]'`` |Matches a list trait on *object* named **foo**. | +--------------------------+--------------------------------------------------+ |``'([left,right]).name*'``|Matches the **name** trait of each tree node | | |object that is linked from the **left** or | | |**right** traits of a parent node, starting with | | |the current object as the root node. This pattern | | |also matches the **name** trait of the current | | |object, as the **left** and **right** modifiers | | |are optional. | +--------------------------+--------------------------------------------------+ |``'+dirty'`` |Matches any trait on the current object that has a| | |metadata attribute named **dirty** set. | +--------------------------+--------------------------------------------------+ |``'foo.+dirty'`` |Matches any trait on *object*.\ **foo** that has a| | |metadata attribute named **dirty** set. | +--------------------------+--------------------------------------------------+ |``'foo.[bar,-dirty]'`` |Matches *object*.\ **foo.bar** or any trait on | | |*object*.\ **foo** that does not have a metadata | | |attribute named **dirty** set. | +--------------------------+--------------------------------------------------+ For a pattern that references multiple objects, any of the intermediate (non-final) links can be traits of type Instance, List, or Dict. In the case of List or Dict traits, the subsequent portion of the pattern is applied to each item in the list or value in the dictionary. For example, if **self.children** is a list, a handler set for ``'children.name'`` listens for changes to the **name** trait for each item in the **self.children** list. The handler routine is also invoked when items are added or removed from a list or dictionary, because this is treated as an implied change to the item's trait being monitored. .. index:: notification; dynamic .. _notification-handler-signatures: Notification Handler Signatures ``````````````````````````````` The handler passed to on_trait_change() or on_trait_event() can have any one of the following signatures: .. index:: handler; signatures, trait change handler; signatures - handler() - handler(*new*) - handler(*name*, *new*) - handler(*object*, *name*, *new*) - handler(*object*, *name*, *old*, *new*) These signatures use the following parameters: .. index:: object parameter; notification handlers * *object*: The object whose trait attribute changed. .. index:: name parameter; notification handlers * *name*: The attribute that changed. If one of the objects in a sequence is a List or Dict, and its membership changes, then this is the name of the trait that references it, with '_items appended. For example, if the handler is monitoring ``'foo.bar.baz'``, where **bar** is a List, and an item is added to **bar**, then the value of the *name* parameter is 'bar_items'. .. index:: new parameter to the notification handlers * *new*: The new value of the trait attribute that changed. For changes to List and Dict objects, this is a list of items that were added. .. index:: old parameter to the notification handlers * *old*: The old value of the trait attribute that changed. For changes to List and Dict object, this is a list of items that were deleted. For event traits, this is Undefined. If the handler is a bound method, it also implicitly has *self* as a first argument. .. index:: notification; special cases .. _dynamic-handler-special-cases: Dynamic Handler Special Cases ````````````````````````````` In the one- and two-parameter signatures, the handler does not receive enough information to distinguish between a change to the final trait attribute being monitored, and a change to an intermediate object. In this case, the notification dispatcher attempts to map a change to an intermediate object to its effective change on the final trait attribute. This mapping is only possible if all the intermediate objects are single values (such as Instance or Any traits), and not List or Dict traits. If the change involves a List or Dict, then the notification dispatcher raises a TraitError when attempting to call a one- or two-parameter handler function, because it cannot unambiguously resolve the effective value for the final trait attribute. Zero-parameter signature handlers receive special treatment if the final trait attribute is a List or Dict, and if the string used for the *name* parameter is not just a simple trait name. In this case, the handler is automatically called when the membership of a final List or Dict trait is changed. This behavior can be useful in cases where the handler needs to know only that some aspect of the final trait has changed. For all other signatures, the handler function must be explicitly set for the *name*\ _items trait in order to called when the membership of the name trait changes. (Note that the *prefix*\ + and *item*\ [] syntaxes are both ways to specify both a trait name and its '_items' variant.) This behavior for zero-parameter handlers is not triggered for simple trait names, to preserve compatibility with code written for versions of Traits prior to 3.0. Earlier versions of Traits required handlers to be separately set for a trait and its items, which would result in redundant notifications under the Traits 3.0 behavior. Earlier versions also did not support the extended trait name syntax, accepting only simple trait names. Therefore, to use the "new style" behavior of zero-parameter handlers, be sure to include some aspect of the extended trait name syntax in the name specifier. .. index:: examples; handlers :: # list_notifier.py -- Example of zero-parameter handlers for an object # containing a list from traits.api import HasTraits, List class Employee: pass class Department( HasTraits ): employees = List(Employee) def a_handler(): print "A handler" def b_handler(): print "B handler" def c_handler(): print "C handler" fred = Employee() mary = Employee() donna = Employee() dept = Department(employees=[fred, mary]) # "Old style" name syntax # a_handler is called only if the list is replaced: dept.on_trait_change( a_handler, 'employees' ) # b_handler is called if the membership of the list changes: dept.on_trait_change( b_handler, 'employees_items') # "New style" name syntax # c_handler is called if 'employees' or its membership change: dept.on_trait_change( c_handler, 'employees[]' ) print "Changing list items" dept.employees[1] = donna # Calls B and C print "Replacing list" dept.employees = [donna] # Calls A and C .. index:: notification; static .. _static-notification: Static Notification ------------------- The static approach is the most convenient option, but it is not always possible. Writing a static change notification handler requires that, for a class whose trait attribute changes you are interested in, you write a method on that class (or a subclass). Therefore, you must know in advance what classes and attributes you want notification for, and you must be the author of those classes. Static notification also entails that every instance of the class has the same notification handlers. To indicate that a particular method is a static notification handler for a particular trait, you have two options: .. index:: pair: decorator; on_trait_change * Apply the @on_trait_change decorator to the method. * Give the method a special name based on the name of the trait attribute it "listens" to. .. _handler-decorator: Handler Decorator ````````````````` The most flexible method of statically specifying that a method is a notification handler for a trait is to use the @on_trait_change() decorator. The @on_trait_change() decorator is more flexible than specially-named method handlers, because it supports the very powerful extended trait name syntax (see :ref:`the-name-parameter`). You can use the decorator to set handlers on multiple attributes at once, on trait attributes of linked objects, and on attributes that are selected based on trait metadata. .. index:: pair: on_trait_change; syntax .. _decorator-syntax: Decorator Syntax :::::::::::::::: The syntax for the decorator is:: @on_trait_change( 'extended_trait_name' ) def any_method_name( self, ...): ... In this case, *extended_trait_name* is a specifier for one or more trait attributes, using the syntax described in :ref:`the-name-parameter`. The signatures that are recognized for "decorated" handlers are the same as those for dynamic notification handlers, as described in :ref:`notification-handler-signatures`. That is, they can have an *object* parameter, because they can handle notifications for trait attributes that do not belong to the same object. .. index:: pair: on_trait_change; semantics .. _decorator-semantics: Decorator Semantics ::::::::::::::::::: The functionality provided by the @on_trait_change() decorator is identical to that of specially-named handlers, in that both result in a call to on_trait_change() to register the method as a notification handler. However, the two approaches differ in when the call is made. Specially-named handlers are registered at class construction time; decorated handlers are registered at instance creation time, prior to setting any object state. A consequence of this difference is that the @on_trait_change() decorator causes any default initializers for the traits it references to be executed at instance construction time. In the case of specially-named handlers, any default initializers are executed lazily. .. index:: notification; specially-named handlers .. _specially-named-notification-handlers: Specially-named Notification Handlers ````````````````````````````````````` There are two kinds of special method names that can be used for static trait attribute change notifications. One is attribute-specific, and the other applies to all trait attributes on a class. .. index:: _name_changed(), _name_fired() To notify about changes to a single trait attribute named name, define a method named _\ *name*\ _changed() or _\ *name*\ _fired(). The leading underscore indicates that attribute-specific notification handlers are normally part of a class's private API. Methods named _\ *name*\ _fired() are normally used with traits that are events, described in :ref:`trait-events`. To notify about changes to any trait attribute on a class, define a method named _anytrait_changed(). .. index:: pair: examples; _any_trait_changed() pair: static notification; examples Both of these types of static trait attribute notification methods are illustrated in the following example:: # static_notification.py --- Example of static attribute # notification from traits.api import HasTraits, Float class Person(HasTraits): weight_kg = Float(0.0) height_m = Float(1.0) bmi = Float(0.0) def _weight_kg_changed(self, old, new): print 'weight_kg changed from %s to %s ' % (old, new) if self.height_m != 0.0: self.bmi = self.weight_kg / (self.height_m**2) def _anytrait_changed(self, name, old, new): print 'The %s trait changed from %s to %s ' \ % (name, old, new) """ >>> bob = Person() >>> bob.height_m = 1.75 The height_m trait changed from 1.0 to 1.75 >>> bob.weight_kg = 100.0 The weight_kg trait changed from 0.0 to 100.0 weight_kg changed from 0.0 to 100.0 The bmi trait changed from 0.0 to 32.6530612245 """ In this example, the attribute-specific notification function is _weight_kg_changed(), which is called only when the **weight_kg** attribute changes. The class-specific notification handler is _anytrait_changed(), and is called when **weight_kg**, **height_m**, or **bmi** changes. Thus, both handlers are called when the **weight_kg** attribute changes. Also, the _weight_kg_changed() function modifies the **bmi** attribute, which causes _anytrait_changed() to be called for that attribute. The arguments that are passed to the trait attribute change notification method depend on the method signature and on which type of static notification handler it is. .. _attribute-specific-handler-signatures: Attribute-specific Handler Signatures ````````````````````````````````````` For an attribute specific notification handler, the method signatures supported are: .. method:: _name_changed() .. method:: _name_changed(new) .. method:: _name_changed(old, new) .. method:: _name_changed(name, old, new) The method name can also be _\ *name*\ _fired(), with the same set of signatures. In these signatures: * *new* is the new value assigned to the trait attribute. For List and Dict objects, this is a list of the items that were added. * *old* is the old value assigned to the trait attribute. For List and Dict objects, this is a list of the items that were deleted. * *name* is the name of the trait attribute. The extended trait name syntax is not supported. [4]_ Note that these signatures follow a different pattern for argument interpretation from dynamic handlers and decorated static handlers. Both of the following methods define a handler for an object's **name** trait:: def _name_changed( self, arg1, arg2, arg3): pass @on_trait_change('name') def some_method( self, arg1, arg2, arg3): pass However, the interpretation of arguments to these methods differs, as shown in the following table. .. _handler-argument-interpretation-table: .. rubric:: Handler argument interpretation ======== =================== ================ Argument _\ *name*\ _changed @on_trait_change ======== =================== ================ *arg1* *name* *object* *arg2* *old* *name* *arg3* *new* *new* ======== =================== ================ .. _general-static-handler-signatures: General Static Handler Signatures ````````````````````````````````` In the case of a non-attribute specific handler, the method signatures supported are: .. method:: _anytrait_changed() .. method:: _anytrait_changed(name) .. method:: _anytrait_changed(name, new) .. method:: _anytrait_changed(name, old, new) The meanings for *name*, *new*, and *old* are the same as for attribute-specific notification functions. .. _trait-events: Trait Events ------------ .. index:: events The Traits package defines a special type of trait called an event. Events are instances of (subclasses of) the Event class. There are two major differences between a normal trait and an event: * All notification handlers associated with an event are called whenever any value is assigned to the event. A normal trait attribute only calls its associated notification handlers when the previous value of the attribute is different from the new value being assigned to it. * An event does not use any storage, and in fact does not store the values assigned to it. Any value assigned to an event is reported as the new value to all associated notification handlers, and then immediately discarded. Because events do not retain a value, the *old* argument to a notification handler associated with an event is always the special Undefined object (see :ref:`undefined-object`). Similarly, attempting to read the value of an event results in a TraitError exception, because an event has no value. .. index:: pair: events; examples As an example of an event, consider:: # event.py --- Example of trait event from traits.api import Event, HasTraits, List, Tuple point_2d = Tuple(0, 0) class Line2D(HasTraits): points = List(point_2d) line_color = RGBAColor('black') updated = Event def redraw(self): pass # Not implemented for this example def _points_changed(self): self.updated = True def _updated_fired(self): self.redraw() In support of the use of events, the Traits package understands attribute-specific notification handlers with names of the form _\ *name*\ _fired(), with signatures identical to the _\ *name*\ _changed() functions. In fact, the Traits package does not check whether the trait attributes that _\ *name*\ _fired() handlers are applied to are actually events. The function names are simply synonyms for programmer convenience. Similarly, a function named on_trait_event() can be used as a synonym for on_trait_change() for dynamic notification. .. index:: Undefined object .. _undefined-object: Undefined Object ```````````````` Python defines a special, singleton object called None. The Traits package introduces an additional special, singleton object called Undefined. The Undefined object is used to indicate that a trait attribute has not yet had a value set (i.e., its value is undefined). Undefined is used instead of None, because None is often used for other meanings, such as that the value is not used. In particular, when a trait attribute is first assigned a value and its associated trait notification handlers are called, Undefined is passed as the value of the old parameter to each handler, to indicate that the attribute previously had no value. Similarly, the value of a trait event is always Undefined. .. rubric:: Footnotes .. [4] For List and Dict trait attributes, you can define a handler with the name _\ *name*\ _items_changed(), which receives notifications of changes to the contents of the list or dictionary. This feature exists for backward compatibility. The preferred approach is to use the @on_trait_change decorator with extended name syntax. For a static _\ *name*\ _items_changed() handler, the *new* parameter is a TraitListEvent or TraitDictEvent whose **index**, **added**, and **removed** attributes indicate the nature of the change, and the *old* parameter is Undefined. traits-4.6.0/docs/source/traits_user_manual/testing.rst000066400000000000000000000116671300633736300234060ustar00rootroot00000000000000.. index:: testing, test trait classes Testing ======= .. _testing_trait_classes: Testing Traits Classes ---------------------- A mixin class is provided to facilitate writing tests for HasTraits classes. The following methods are available when |UnittestTools| is added as a mixin class in the developer's test cases. .. autosummary:: :nosignatures: ~traits.testing.unittest_tools.UnittestTools.assertTraitChanges ~traits.testing.unittest_tools.UnittestTools.assertTraitDoesNotChange ~traits.testing.unittest_tools.UnittestTools.assertMultiTraitChanges ~traits.testing.unittest_tools.UnittestTools.assertTraitChangesAsync ~traits.testing.unittest_tools.UnittestTools.assertEventuallyTrue The above assert methods, except |assertEventuallyTrue|, can be used as context managers, which at entry, hook a trait listeners on the class for the desired events and record the arguments passed to the change handler at every fired event. This way the developer can easily assert that specific events have been fired. Further analysis and checking can be performed by inspecting the list of recorded events. Both normal and extended trait names are supported. However, no check is performed regarding the validity of the trait name, thus care is required to safeguard against spelling mistakes in the names of the traits that we need to assert the behaviour. The following example demonstrates the basic usage of the mixin class in a TestCase:: import unittest from traits.api import HasTraits, Float, List, Bool, on_trait_change from traits.testing.api import UnittestTools class MyClass(HasTraits): number = Float(2.0) list_of_numbers = List(Float) flag = Bool @on_trait_change('number') def _add_number_to_list(self, value): """ Append the value to the list of numbers. """ self.list_of_numbers.append(value) def add_to_number(self, value): """ Add the value to `number`. """ self.number += value class MyTestCase(unittest.TestCase, UnittestTools): def setUp(self): self.my_class = MyClass() def test_when_using_with(self): """ Check normal use cases as a context manager. """ my_class = self.my_class # Checking for change events with self.assertTraitChanges(my_class, 'number') as result: my_class.number = 5.0 # Inspecting the last recorded event expected = (my_class, 'number', 2.0, 5.0) self.assertSequenceEqual(result.events, [expected]) # Checking for specific number of events with self.assertTraitChanges(my_class, 'number', count=3) as result: my_class.flag = True my_class.add_to_number(10.0) my_class.add_to_number(10.0) my_class.add_to_number(10.0) expected = [(my_class, 'number', 5.0, 15.0), (my_class, 'number', 15.0, 25.0), (my_class, 'number', 25.0, 35.0)] self.assertSequenceEqual(result.events, expected) # Check using extended names with self.assertTraitChanges(my_class, 'list_of_numbers[]'): my_class.number = -3.0 # Check that event is not fired my_class.number = 2.0 with self.assertTraitDoesNotChange(my_class, 'number') as result: my_class.flag = True my_class.number = 2.0 # The value is the same as the original Using Mocks ----------- Trying to mock a method in a |HasStrictTraits| instance will raise an error because the |HasStrictTraits| machinery does not allow any modification of the methods and attributes of a |HasStrictTraits| instance. To circumvent the |HasStrictTraits| machinery, and mock methods using `the mock library`_, please follow the logic in the example below:: from traits.api import HasStrictTraits, Float from mock import Mock class MyClass(HasStrictTraits): number = Float(2.0) def add_to_number(self, value): """ Add the value to `number`. """ self.number += value my_class = MyClass() # Using my_class.add_to_number = Mock() will fail. # But setting the mock on the instance `__dict__` works. my_class.__dict__['add_to_number'] = Mock() # We can now use the mock in our tests. my_class.add_number(42) print my_class.add_to_number.call_args_list .. note:: The above method will not work for mocking |Property| setters, getters and validators. .. _the mock library: https://pypi.python.org/pypi/mock .. |HasStrictTraits| replace:: :class:`~traits.has_traits.HasStrictTraits` .. |UnittestTools| replace:: :class:`~traits.testing.unittest_tools.UnittestTools` .. |Property| replace:: :func:`~traits.traits.Property` .. |assertEventuallyTrue| replace:: :func:`~traits.testing.unittest_tools.UnittestTools.assertEventuallyTrue` traits-4.6.0/examples/000077500000000000000000000000001300633736300146515ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/000077500000000000000000000000001300633736300166775ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/default.css000066400000000000000000000001111300633736300210260ustar00rootroot00000000000000pre { border: 1px solid black; background-color: #E8E8E8; padding: 4px } traits-4.6.0/examples/tutorials/doc_examples/000077500000000000000000000000001300633736300213425ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/doc_examples/default.css000066400000000000000000000015001300633736300234740ustar00rootroot00000000000000body { background-color: #FFFFFF; } h1 { font-family: Arial; font-size: 14pt; color: #303030; background-color: #FCD062; padding-top: 3px; padding-left:8px; padding-bottom: 3px; padding-right: 8px; } h2 { font-family: Arial; font-size: 12pt; color: #303030; background-color: #FCD062; padding-top: 3px; padding-left:8px; padding-bottom: 3px; padding-right: 8px; } pre { border: 1px solid #A0A0A0; background-color: #FDF7E7; padding: 4px; } dl { border: 1px solid #A0A0A0; background-color: #FDF7E7; padding-top: 4px; padding-bottom: 6px; padding-left: 8px; padding-right: 8px; } dl dl { border: 0px solid #A0A0A0; } dt { font-family: Arial; font-weight: bold; dd { padding-bottom: 10px; } traits-4.6.0/examples/tutorials/doc_examples/doc_examples.rst000066400000000000000000000041561300633736300245450ustar00rootroot00000000000000Documentation Examples ====================== This tutorial is simply a collection of the various examples taken from the Traits documentation. They are presented as a tutorial so that interested users can see the results of actually executing the example code, as well as to promote further study and exploration of various Traits topics in an interactive environment that allows easy modification and testing of changes to the code. So please feel free to explore the examples as your time, fancy and interest dictate. Have fun! A brief description of each of the examples presented in this tutorial follows: Add Class Traits Defining mutually-referring classes using the *add_class_trait* method. All Traits Features Shows the five primary features of the Traits package. Bad Self Ref Shows the incorrect way of defining a self-referencing class. Cast Simple Shows use of some of the simple casting types. Circular Definition Shows the incorrect way of defining mutually-referring classes. Compound Shows the definition of a compound trait. Custom TraitHandler Example of a custom TraitHandler Use Custom TraitHandler Example of using a custom TraitHandler Delegate Example of trait delegation. Delegate Prefix Examples of the Delegate() 'prefix' parameter. Delegation Notification Example of notification with delegation. Event Shows the definition of a trait event List Notifiers Example of zero-parameter handlers for an object containing a list Metadata Example of accessing trait metadata attributes Minimal Minimal example of using Traits. Override Default Example of overriding a default value for a trait attribute in a subclass Static Notification Example of static attribute notification This Example of This predefined trait Trait Reuse Example of reusing trait definitions Temp Wildcard Example of using a wildcard with a trait attribute name. All Wildcard Shows the trait attribute wildcard rules. Wildcard rules Example of trait attribute wildcard rules traits-4.6.0/examples/tutorials/doc_examples/examples/000077500000000000000000000000001300633736300231605ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/doc_examples/examples/adapt_metadata.py000066400000000000000000000006621300633736300264670ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # adapt_metadata.py - Example of using 'adapt' metadata #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Instance from interface_definition import IName #--[Code]---------------------------------------------------------------------- class Apartment(HasTraits): renter = Instance(IName, adapt='no') traits-4.6.0/examples/tutorials/doc_examples/examples/add_class_trait.py000066400000000000000000000013001300633736300266440ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # add_class_trait.py --- Example of mutually-referring classes # using add_class_trait() #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Instance #--[Code]---------------------------------------------------------------------- # Defining mutually-referring classes using add_class_trait() class Chicken(HasTraits): pass class Egg(HasTraits): created_by = Instance(Chicken) # Now that 'Egg' is defined, we can add the 'hatched_from' trait to # solve the mutual-reference problem... Chicken.add_class_trait('hatched_from', Instance(Egg)) traits-4.6.0/examples/tutorials/doc_examples/examples/all_traits_features.py000066400000000000000000000026311300633736300275700ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # all_traits_features.py --- Shows primary features of the Traits # package #--[Imports]------------------------------------------------------------------- from traits.api import Delegate, HasTraits, Instance, Int, Str #--[Code]---------------------------------------------------------------------- # Shows the five primary features of the Traits package. class Parent(HasTraits): # INITIALIZATION: last_name' is initialized to '': last_name = Str('') class Child(HasTraits): age = Int # VALIDATION: 'father' must be a Parent instance: father = Instance(Parent) # DELEGATION: 'last_name' is delegated to father's 'last_name': last_name = Delegate('father') # NOTIFICATION: This method is called when 'age' changes: def _age_changed(self, old, new): print 'Age changed from %s to %s ' % (old, new) #--[Example*]------------------------------------------------------------------ # Set up the example: joe = Parent() joe.last_name = 'Johnson' moe = Child() moe.father = joe # DELEGATION in action: print "Moe's last name is %s " % moe.last_name # Result: # Moe's last name is Johnson # NOTIFICATION in action moe.age = 10 # Result: # Age changed from 0 to 10 # VISUALIZATION: Displays a UI for editing moe's # attributes (if a supported GUI toolkit is installed) moe.configure_traits() traits-4.6.0/examples/tutorials/doc_examples/examples/all_wildcard.py000066400000000000000000000020521300633736300261520ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # all_wildcard.py --- Example of trait attribute wildcard rules #--[Imports]------------------------------------------------------------------- from traits.api import Any, Str, Int, HasTraits, TraitError #--[Code]---------------------------------------------------------------------- class Person(HasTraits): # Normal, explicitly defined trait: name = Str # By default, let all traits have any value: _ = Any # Except for this one, which must be an Int: age = Int #--[Example*]------------------------------------------------------------------ # Create a sample Person: bill = Person() # These assignments should all work: bill.name = 'William' bill.address = '121 Drury Lane' bill.zip_code = 55212 bill.age = 49 # This should generate an error (must be an Int): print 'Attempting to assign a string to an Int trait object...\n' try: bill.age = 'middle age' except TraitError, c: print 'TraitError: ', c, '\n' # Display the final results: bill.print_traits() traits-4.6.0/examples/tutorials/doc_examples/examples/bad_self_ref.py000066400000000000000000000011641300633736300261270ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # bad_self_ref.py -- Non-working example with self-referencing class definition #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Instance #--[Code]---------------------------------------------------------------------- # Shows the incorrect way of defining a self-referencing class. try: class Employee(HasTraits): # This won't work. # 'Employee' is not defined until the class definition is complete: manager = Instance(Employee) except NameError, excp: print excp traits-4.6.0/examples/tutorials/doc_examples/examples/cached_prop.py000066400000000000000000000016031300633736300260010ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # cached_prop.py - Example of @cached_property decorator #--[Imports]------------------------------------------------------------------- from traits.api import HasPrivateTraits, List, Int, Property, cached_property #--[Code]---------------------------------------------------------------------- class TestScores(HasPrivateTraits): scores = List(Int) average = Property(depends_on='scores') @cached_property def _get_average(self): s = self.scores return float(reduce(lambda n1, n2: n1 + n2, s, 0)) / len(s) traits-4.6.0/examples/tutorials/doc_examples/examples/circular_definition.py000066400000000000000000000012511300633736300275450ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Instance #--[Code]---------------------------------------------------------------------- # Shows the incorrect way of defining mutually-referring classes. try: class Chicken(HasTraits): # Won't work: 'Egg' not defined yet: hatched_from = Instance(Egg) class Egg(HasTraits): # If we move this class to the top, then this line won't work, because # 'Chicken' won't be defined yet: created_by = Instance(Chicken) except NameError, excp: print excp traits-4.6.0/examples/tutorials/doc_examples/examples/compound.py000066400000000000000000000016671300633736300253700ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # compound.py -- Example of multiple criteria in a trait definition #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Range, Trait, TraitError #--[Code]---------------------------------------------------------------------- # Shows the definition of a compound trait. class Die(HasTraits): # Define a compound trait definition: value = Trait(1, Range(1, 6), 'one', 'two', 'three', 'four', 'five', 'six') #--[Example*]------------------------------------------------------------------ # Create a sample Die: die = Die() # Try out some sample valid values: die.value = 3 die.value = 'three' die.value = 5 die.value = 'five' # Now try out some invalid values: try: die.value = 0 except TraitError, excp: print excp try: die.value = 'zero' except TraitError, excp: print excp traits-4.6.0/examples/tutorials/doc_examples/examples/configure_traits.py000066400000000000000000000010161300633736300270770ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # configure_traits.py -- Sample code to demonstrate configure_traits() #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Str, Int #--[Code]---------------------------------------------------------------------- class SimpleEmployee(HasTraits): first_name = Str last_name = Str department = Str employee_number = Str salary = Int sam = SimpleEmployee() sam.configure_traits() traits-4.6.0/examples/tutorials/doc_examples/examples/custom_traithandler.py000066400000000000000000000011541300633736300276060ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # custom_traithandler.py --- Example of a custom TraitHandler #--[Imports]------------------------------------------------------------------- from traits.api import TraitHandler #--[Code]---------------------------------------------------------------------- class TraitOddInteger(TraitHandler): def validate(self, object, name, value): if (isinstance(value, int) and (value > 0) and ((value % 2) == 1)): return value self.error(object, name, value) def info(self): return 'a positive odd integer' traits-4.6.0/examples/tutorials/doc_examples/examples/deferring_notification.py000066400000000000000000000031441300633736300302470ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # deferring_notification.py -- Example of notification with deferring #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Instance, PrototypedFrom, Str #--[Code]---------------------------------------------------------------------- class Parent(HasTraits): first_name = Str last_name = Str def _last_name_changed(self, new): print "Parent's last name changed to %s." % new class Child(HasTraits): father = Instance(Parent) first_name = Str last_name = PrototypedFrom('father') def _last_name_changed(self, new): print "Child's last name changed to %s." % new #--[Example*]------------------------------------------------------------------ dad = Parent(first_name='William', last_name='Chase') # Output: Parent's last name changed to Chase. son = Child(first_name='John', father=dad) # Output: Child's last name changed to Chase. # Change Parent's last_name dad.last_name = 'Jones' # Output: Parent's last name changed to Jones. # Child's last name changed to Jones. # Override Child's last_name son.last_name = 'Thomas' # Output Child's last name changed to Thomas. # Change Parent's last_name; Child's is not affected. dad.last_name = 'Riley' # Output: Parent's last name changed to Riley. # Reset Child's last_name del son.last_name # Output: Child's last name changed to Riley. # Change to Parent now affects Child. dad.last_name = 'Simmons' # Output: Parent's last name changed to Simmons. # Child's last name changed to Simmons. traits-4.6.0/examples/tutorials/doc_examples/examples/delegate.py000066400000000000000000000033001300633736300253000ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # delegate.py --- Example of trait delegation #--[Imports]------------------------------------------------------------------- from traits.api import DelegatesTo, HasTraits, Instance, Str, TraitError #--[Code]---------------------------------------------------------------------- class Parent(HasTraits): first_name = Str last_name = Str class Child(HasTraits): first_name = Str last_name = DelegatesTo('father') father = Instance(Parent) mother = Instance(Parent) #--[Example*]------------------------------------------------------------------ tony = Parent(first_name='Anthony', last_name='Jones') alice = Parent(first_name='Alice', last_name='Smith') sally = Child(first_name='Sally', father=tony, mother=alice) # Child delegates its 'last_name' to its 'father' object's 'last_name' print sally.last_name # Output: Jones # Assign an explicit value to the child's 'last_name' sally.last_name = 'Cooper' print tony.last_name #Output: Cooper # Validation is still controlled by the father's 'last_name' trait print 'Attempting to assign a Parent object to a Str trait...\n' try: sally.last_name = sally.mother # ERR: string expected except TraitError, c: print 'TraitError: ', c """ The exception printed will look similar to the following: Traceback (most recent call last): File "", line 1, in ? File "c:\src\trunk\enthought\traits\trait_handlers.py", line 163, in error raise TraitError, ( object, name, self.info(), value ) traits.trait_errors.TraitError: The 'last_name' trait of a Child instance must be a value of type 'str', but a value of <__main__.Parent object at 0x009DD6F0> was specified. """ traits-4.6.0/examples/tutorials/doc_examples/examples/disallow.py000066400000000000000000000006631300633736300253550ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # disallow.py --- Example of using Disallow with wildcards #--[Imports]------------------------------------------------------------------- from traits.api import Disallow, Float, HasTraits, Int, Str #--[Code]---------------------------------------------------------------------- class Person(HasTraits): name = Str age = Int weight = Float _ = Disallow traits-4.6.0/examples/tutorials/doc_examples/examples/dynamic_notification.py000066400000000000000000000016451300633736300277320ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # dynamic_notification.py --- Example of dynamic notification #--[Imports]------------------------------------------------------------------- from traits.api import Float, HasTraits, Instance #--[Code]---------------------------------------------------------------------- class Part(HasTraits): cost = Float(0.0) class Widget(HasTraits): part1 = Instance(Part) part2 = Instance(Part) cost = Float(0.0) def __init__(self): self.part1 = Part() self.part2 = Part() self.part1.on_trait_change(self.update_cost, 'cost') self.part2.on_trait_change(self.update_cost, 'cost') def update_cost(self): self.cost = self.part1.cost + self.part2.cost #--[Example*]------------------------------------------------------------------ w = Widget() w.part1.cost = 2.25 w.part2.cost = 5.31 print w.cost # Result: 7.56 traits-4.6.0/examples/tutorials/doc_examples/examples/event.py000066400000000000000000000012071300633736300246530ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # event.py --- Example of a trait event #--------------------------------------------------------------------- from traits.api import Event, HasTraits, List, RGBColor, Tuple #--[Code]---------------------------------------------------------------------- point_2d = Tuple(0, 0) class Line2D(HasTraits): points = List(point_2d) line_color = RGBColor('black') updated = Event def redraw(self): pass # Not implemented for this example def _points_changed(self): self.updated = True def _updated_fired(self): self.redraw() traits-4.6.0/examples/tutorials/doc_examples/examples/external_adapter.py000066400000000000000000000014761300633736300270640ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # external_adapter.py - Example of declaring a class as an adapter # externally to the class #--[Imports]------------------------------------------------------------------- from traits.api import adapts from interface_definition import IName from interface_implementation import Person #--[Code]---------------------------------------------------------------------- class AnotherPersonAdapter(object): # Implement the adapter's constructor: def __init__(self, person): self.person = person # Implement the 'IName' interface on behalf of its client: def get_name(self): return ('%s %s' % (self.person.first_name, self.person.last_name)) adapts(AnotherPersonAdapter, Person, IName) traits-4.6.0/examples/tutorials/doc_examples/examples/interface_definition.py000066400000000000000000000006621300633736300277060ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # interface_definition.py - Example of defining an interface #--[Imports]------------------------------------------------------------------- from traits.api import Interface #--[Code]---------------------------------------------------------------------- class IName(Interface): def get_name(self): """ Returns a string which is the name of an object. """ traits-4.6.0/examples/tutorials/doc_examples/examples/interface_implementation.py000066400000000000000000000017341300633736300306040ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # interface_implementation.py - Example of implementing an interface #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, implements, Str, Instance from interface_definition import IName #--[Code]---------------------------------------------------------------------- class Person(HasTraits): implements(IName) first_name = Str('John') last_name = Str('Doe') # Implementation of the 'IName' interface: def get_name(self): """ Returns the name of an object. """ return ('%s %s' % (self.first_name, self.last_name)) #--[Example*]------------------------------------------------------------------ class Apartment(HasTraits): renter = Instance(IName) william = Person(first_name='William', last_name='Adams') apt1 = Apartment(renter=william) print 'Renter is: ', apt1.renter.get_name() # Result: Renter is: William Adams traits-4.6.0/examples/tutorials/doc_examples/examples/keywords.py000066400000000000000000000012131300633736300253760ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # keywords.py --- Example of trait keywords #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Str #--[Code]---------------------------------------------------------------------- class Person(HasTraits): # 'label' is used for Traits UI field labels; # 'desc' can be used for tooltips. first_name = Str('', desc='first or personal name', label='First Name') last_name = Str('', desc='last or family name', label='Last Name') traits-4.6.0/examples/tutorials/doc_examples/examples/lesson.desc000066400000000000000000000015051300633736300253240ustar00rootroot00000000000000Examples from the Traits User Guide all_traits_features all_wildcard bad_self_ref circular_definition add_class_trait cast_simple compound custom_traithandler default_trait_ui delegate delegate_prefix disallow dynamic_notification enum_simple event explicit false graphic_example imageenumeditor include_extra instanceeditor instance_example keywords mapped map_simple minimal multiple_criteria object_trait_attrs override_default override_editor person_bmi range reuse_editor static_notification temp_wildcard this traitcasttype traitdict traitenum traitinstance traitlist traitmap traitprefixlist traitprefixmap traitrange traitstring traittuple traitype trait_reuse type_simple user_custom_th use_custom_th validator view_attributes view_multi_object view_standalone widget wildcard wildcard_all wildcard_name wildcard_rules wizard traits-4.6.0/examples/tutorials/doc_examples/examples/list_notifier.py000066400000000000000000000024021300633736300264020ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # list_notifier.py -- Example of zero-parameter handlers for an object # containing a list #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, List #--[Code]---------------------------------------------------------------------- class Employee: pass class Department(HasTraits): employees = List(Employee) #--[Example*]------------------------------------------------------------------ def a_handler(): print "A handler" def b_handler(): print "B handler" def c_handler(): print "C handler" fred = Employee() mary = Employee() donna = Employee() dept = Department(employees=[fred, mary]) # "Old style" name syntax # a_handler is called only if the list is replaced: dept.on_trait_change(a_handler, 'employees') # b_handler is called if the membership of the list changes: dept.on_trait_change(b_handler, 'employees_items') # "New style" name syntax # c_handler is called if 'employees' or its membership change: dept.on_trait_change(c_handler, '[employees]') print "Changing list items" dept.employees[1] = donna # Calls B and C print "Replacing list" dept.employees = [donna] # Calls A and C traits-4.6.0/examples/tutorials/doc_examples/examples/mapped.py000066400000000000000000000030441300633736300250010ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # mapped.py --- Example of a mapped trait #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Trait #--[Code]---------------------------------------------------------------------- standard_color = Trait('black', { 'black': (0.0, 0.0, 0.0, 1.0), 'blue': (0.0, 0.0, 1.0, 1.0), 'cyan': (0.0, 1.0, 1.0, 1.0), 'green': (0.0, 1.0, 0.0, 1.0), 'magenta': (1.0, 0.0, 1.0, 1.0), 'orange': (0.8, 0.196, 0.196, 1.0), 'purple': (0.69, 0.0, 1.0, 1.0), 'red': (1.0, 0.0, 0.0, 1.0), 'violet': (0.31, 0.184, 0.31, 1.0), 'yellow': (1.0, 1.0, 0.0, 1.0), 'white': (1.0, 1.0, 1.0, 1.0), 'transparent': (1.0, 1.0, 1.0, 0.0) }) red_color = Trait('red', standard_color) class GraphicShape(HasTraits): line_color = standard_color fill_color = red_color #--[Example*]------------------------------------------------------------------ my_shape1 = GraphicShape() # Default values for normal trait attributes print my_shape1.line_color, my_shape1.fill_color # Output: black red # Default values for shadow trait attributes print my_shape1.line_color_, my_shape1.fill_color_ # Output: (0.0, 0.0, 0.0, 1.0) (1.0, 0.0, 0.0, 1.0) # Non-default values my_shape2 = GraphicShape() my_shape2.line_color = 'blue' my_shape2.fill_color = 'green' print my_shape2.line_color, my_shape2.fill_color # Output: blue green print my_shape2.line_color_, my_shape2.fill_color_ # Output: (0.0, 0.0, 1.0, 1.0) (0.0, 1.0, 0.0, 1.0) traits-4.6.0/examples/tutorials/doc_examples/examples/metadata.py000066400000000000000000000036731300633736300253230ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # metadata.py --- Example of accessing trait metadata attributes #--[Imports]------------------------------------------------------------------- from traits.api import (HasTraits, Int, List, Float, Instance, Any, TraitType) #--[Code]---------------------------------------------------------------------- class Foo(HasTraits): pass class Test(HasTraits): i = Int(99) lf = List(Float) foo = Instance(Foo, ()) any = Any([1, 2, 3]) #--[Example*]------------------------------------------------------------------ t = Test() # Prints values of various metadata attributes for each of the traits. print t.trait('i').default # 99 print t.trait('i').default_kind # value print t.trait('i').inner_traits # () print t.trait('i').is_trait_type(Int) # True print t.trait('i').is_trait_type(Float) # False print t.trait('lf').default # [] print t.trait('lf').default_kind # list print t.trait('lf').inner_traits # (,) print t.trait('lf').is_trait_type(List) # True print t.trait('lf').is_trait_type(TraitType) # True print t.trait('lf').is_trait_type(Float) # False print t.trait('lf').inner_traits[0].is_trait_type(Float) # True print t.trait('foo').default # print t.trait('foo').default_kind # factory print t.trait('foo').inner_traits # () print t.trait('foo').is_trait_type(Instance) # True print t.trait('foo').is_trait_type(List) # False print t.trait('any').default # [1, 2, 3] print t.trait('any').default_kind # list print t.trait('any').inner_traits # () print t.trait('any').is_trait_type(Any) # True print t.trait('any').is_trait_type(List) # False traits-4.6.0/examples/tutorials/doc_examples/examples/minimal.py000066400000000000000000000016251300633736300251640ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # minimal.py --- Minimal example of using traits. #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Float, TraitError #--[Code]---------------------------------------------------------------------- class Person(HasTraits): weight = Float(150.0) #--[Example*]------------------------------------------------------------------ # instantiate the class joe = Person() # Show the default value print joe.weight # Assign new values joe.weight = 161.9 # OK to assign a float print joe.weight joe.weight = 162 # OK to assign an int print joe.weight # The following line causes a traceback: try: joe.weight = 'average' # Error to assign a string print "You should not see this message." except TraitError: print "You can't assign a string to the 'weight' trait." traits-4.6.0/examples/tutorials/doc_examples/examples/object_trait_attrs.py000066400000000000000000000016111300633736300274170ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # object_trait_attrs.py --- Example of per-object trait attributes #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Range #--[Code]---------------------------------------------------------------------- class GUISlider(HasTraits): def __init__(self, eval=None, label='Value', trait=None, min=0.0, max=1.0, initial=None, **traits): HasTraits.__init__(self, **traits) if trait is None: if min > max: min, max = max, min if initial is None: initial = min elif not (min <= initial <= max): initial = [min, max][abs(initial - min) > abs(initial - max)] trait = Range(min, max, value=initial) self.add_trait(label, trait) traits-4.6.0/examples/tutorials/doc_examples/examples/override_default.py000066400000000000000000000016321300633736300270570ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # override_default.py -- Example of overriding a default value for # a trait attribute in a subclass from traits.api import HasTraits, Range, Str #--[Code]---------------------------------------------------------------------- # Example of overriding a default value for a trait in a subclass: # Define the base class: class Employee(HasTraits): name = Str salary_grade = Range(value=1, low=1, high=10) # Define a subclass: class Manager(Employee): # Override the default value for the inherited 'salary_grade' trait: salary_grade = 5 #--[Example*]------------------------------------------------------------------ # Create an employee and display its initial contents: joe = Employee(name='Joe') joe.print_traits() # Now do the same thing for a manager object: mike = Manager(name='Mike') mike.print_traits() traits-4.6.0/examples/tutorials/doc_examples/examples/prototype_prefix.py000066400000000000000000000024611300633736300271570ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # prototype_prefix.py --- Examples of PrototypedFrom() prefix parameter #--[Imports]------------------------------------------------------------------- from traits.api import PrototypedFrom, Float, HasTraits, Instance, Str #--[Code]---------------------------------------------------------------------- class Parent(HasTraits): first_name = Str family_name = '' favorite_first_name = Str child_allowance = Float(1.00) class Child(HasTraits): __prefix__ = 'child_' first_name = PrototypedFrom('mother', 'favorite_*') last_name = PrototypedFrom('father', 'family_name') allowance = PrototypedFrom('father', '*') father = Instance(Parent) mother = Instance(Parent) #--[Example*]------------------------------------------------------------------ fred = Parent(first_name='Fred', family_name='Lopez', favorite_first_name='Diego', child_allowance=5.0) maria = Parent(first_name='Maria', family_name='Gonzalez', favorite_first_name='Tomas', child_allowance=10.0) nino = Child(father=fred, mother=maria) print '%s %s gets $%.2f for allowance' % (nino.first_name, nino.last_name, nino.allowance) traits-4.6.0/examples/tutorials/doc_examples/examples/scratch_adapter.py000066400000000000000000000016441300633736300266660ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # scratch_adapter.py - Example of writing an adapter from scratch #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Instance, adapts from interface_definition import IName from interface_implementation import Person #--[Code]---------------------------------------------------------------------- class PersonINameAdapter(HasTraits): # Declare what interfaces this adapter implements, # and for what class: adapts(Person, IName) # Declare the type of client it supports: client = Instance(Person) # Implement the adapter's constructor: def __init__(self, client): self.client = client # Implement the 'IName' interface on behalf of its client: def get_name(self): return ('%s %s' % (self.client.first_name, self.client.last_name)) traits-4.6.0/examples/tutorials/doc_examples/examples/simple_adapter.py000066400000000000000000000014701300633736300265250ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # simple_adapter.py - Example of adaptation using Adapter #--[Imports]------------------------------------------------------------------- from traits.api import Adapter, Instance, implements from interface_definition import IName from interface_implementation import Person #--[Code]---------------------------------------------------------------------- class PersonINameAdapter(Adapter): # Declare what interfaces this adapter implements for its # client: implements(IName) # Declare the type of client it supports: adaptee = Instance(Person) # Implement the 'IName' interface on behalf of its client: def get_name(self): return ('%s %s' % (self.adaptee.first_name, self.adaptee.last_name)) traits-4.6.0/examples/tutorials/doc_examples/examples/static_notification.py000066400000000000000000000021101300633736300275610ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # static_notification.py --- Example of static attribute notification #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Float #--[Code]---------------------------------------------------------------------- class Person(HasTraits): weight_kg = Float(0.0) height_m = Float(1.0) bmi = Float(0.0) def _weight_kg_changed(self, old, new): print 'weight_kg changed from %s to %s ' % (old, new) if self.height_m != 0.0: self.bmi = self.weight_kg / (self.height_m ** 2) def _anytrait_changed(self, name, old, new): print 'The %s trait changed from %s to %s ' % (name, old, new) #--[Example*]------------------------------------------------------------------ bob = Person() bob.height_m = 1.75 # Output: The height_m trait changed from 1.0 to 1.75 bob.weight_kg = 100.0 # Output: # The weight_kg trait changed from 0.0 to 100.0 # weight_kg changed from 0.0 to 100.0 # The bmi trait changed from 0.0 to 32.6530612245 traits-4.6.0/examples/tutorials/doc_examples/examples/temp_wildcard.py000066400000000000000000000006221300633736300263500ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # temp_wildcard.py --- Example of using a wildcard with a trait # attribute name #--[Imports]------------------------------------------------------------------- from traits.api import Any, HasTraits #--[Code]---------------------------------------------------------------------- class Person(HasTraits): temp_ = Any traits-4.6.0/examples/tutorials/doc_examples/examples/this.py000066400000000000000000000005461300633736300245060ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # this.py --- Example of This predefined trait #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, This #--[Code]---------------------------------------------------------------------- class Employee(HasTraits): manager = This traits-4.6.0/examples/tutorials/doc_examples/examples/trait_reuse.py000066400000000000000000000006261300633736300260640ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # trait_reuse.py --- Example of reusing trait definitions from traits.api import HasTraits, Range #--[Code]---------------------------------------------------------------------- coefficient = Range(-1.0, 1.0, 0.0) class quadratic(HasTraits): c2 = coefficient c1 = coefficient c0 = coefficient x = Range(-100.0, 100.0, 0.0) traits-4.6.0/examples/tutorials/doc_examples/examples/trait_subclass.py000066400000000000000000000011111300633736300265460ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # trait_subclass.py -- Example of subclassing a trait class from traits.api import BaseInt #--[Code]---------------------------------------------------------------------- class OddInt(BaseInt): # Define the default value default_value = 1 # Describe the trait type info_text = 'an odd integer' def validate(self, object, name, value): value = super(OddInt, self).validate(object, name, value) if (value % 2) == 1: return value self.error(object, name, value) traits-4.6.0/examples/tutorials/doc_examples/examples/traitprefixmap.py000066400000000000000000000010641300633736300265720ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # traitprefixmap.py --- Example of using the TraitPrefixMap handler #--[Imports]------------------------------------------------------------------- from traits.api import Trait, TraitPrefixMap #--[Code]---------------------------------------------------------------------- boolean_map = Trait('true', TraitPrefixMap({ 'true': 1, 'yes': 1, 'false': 0, 'no': 0}) ) traits-4.6.0/examples/tutorials/doc_examples/examples/transient_metadata.py000066400000000000000000000007761300633736300274130ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # transient_metadata.py - Example of using 'transient' metadata #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, File, Any #--[Code]---------------------------------------------------------------------- class DataBase(HasTraits): # The name of the data base file: file_name = File # The open file handle used to access the data base: file = Any(transient=True) traits-4.6.0/examples/tutorials/doc_examples/examples/use_custom_th.py000066400000000000000000000010411300633736300264070ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # use_custom_th.py --- Example of using a custom TraitHandler #--[Imports]------------------------------------------------------------------- from traits.api import HasTraits, Trait, TraitRange from custom_traithandler import TraitOddInteger #--[Code]---------------------------------------------------------------------- class AnOddClass(HasTraits): oddball = Trait(1, TraitOddInteger()) very_odd = Trait(-1, TraitOddInteger(), TraitRange(-10, -1)) traits-4.6.0/examples/tutorials/doc_examples/examples/widget.py000066400000000000000000000010331300633736300250120ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from traits.api import Float, HasTraits, Trait class Part(HasTraits): cost = Trait(0.0) class Widget(HasTraits): part1 = Trait(Part) part2 = Trait(Part) cost = Float(0.0) def __init__(self): self.part1 = Part() self.part2 = Part() self.part1.on_trait_change(self.update_cost, 'cost') self.part2.on_trait_change(self.update_cost, 'cost') def update_cost(self): self.cost = self.part1.cost + self.part2.cost traits-4.6.0/examples/tutorials/doc_examples/examples/wildcard.py000066400000000000000000000003541300633736300253250ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # wildcard.py --- Example of using a wildcard with a trait # attribute name from traits.api import Any, HasTraits class Person(HasTraits): temp_ = Any traits-4.6.0/examples/tutorials/doc_examples/examples/wildcard_all.py000066400000000000000000000003631300633736300261550ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # wildcard_all.py --- Example of using a wildcard with all trait # attribute names from traits.api import HasTraits, Any class Person(HasTraits): _ = Any traits-4.6.0/examples/tutorials/doc_examples/examples/wildcard_name.py000066400000000000000000000003661300633736300263300ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # temp_wildcard.py --- Example of using a wildcard # with a trait attribute name from traits.api import Any, HasTraits class Person(HasTraits): temp_ = Any traits-4.6.0/examples/tutorials/doc_examples/examples/wildcard_rules.py000066400000000000000000000006501300633736300265360ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # wildcard_rules.py --- Example of trait attribute wildcard rules #--[Imports]------------------------------------------------------------------- from traits.api import Any, HasTraits, Int, Python #--[Code]---------------------------------------------------------------------- class Person(HasTraits): temp_count = Int(-1) temp_ = Any _ = Python traits-4.6.0/examples/tutorials/doc_examples/examples/wizard.py000066400000000000000000000005121300633736300250300ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # wizard.py ---Example of a traits-based wizard UI from traits.api import HasTraits, Int, Str class Person(HasTraits): name = Str age = Int street = Str city = Str state = Str pcode = Str bill = Person() bill.configure_traits(kind='modal') traits-4.6.0/examples/tutorials/doc_examples/lesson.desc000066400000000000000000000000651300633736300235060ustar00rootroot00000000000000The Traits Documentation Examples Tutorial examples traits-4.6.0/examples/tutorials/images/000077500000000000000000000000001300633736300201445ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/images/image_LICENSE.txt000066400000000000000000000014331300633736300231320ustar00rootroot00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: Project License File ---------------------------------------------------------------------------- Nuvola LGPL image_LICENSE_Nuvola.txt Unless stated in this file, icons are the work of Enthought, and are released under a 3 clause BSD license. Files and orginal authors: ---------------------------------------------------------------------------- examples/tutorials/images: next.png | Nuvola parent.png | Nuvola previous.png | Nuvola reload.png | Nuvola run.png | Nuvola traits-4.6.0/examples/tutorials/images/next.png000066400000000000000000000016301300633736300216300ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<*IDATxb?% X@ ϿY&R|l~sßXJa`"bWaeAs}TB41?_l. & A@ _~HaOys$2끮 ^`bbaecb`b?+?B_bX=XBB-={; AhS3 cgca'C/ ӦȖ_{AV󯫙, W>2}c tI 1LPP` Z^{ O_|eY  \\ <  ϲ#ݖg@M ?2| , 1e@̌X|q10p3,Ÿ́^3Õ?޽F/_( fl#'y4 擯 A6bAV !v)O޾"Ő`|КJ@zlKfRed`w?8M gs22ԇI2J1t5` 000@1p2&DϿ{I?I QG]}6001<!@e`xC[sW 0\FOĂ' l!Oow3ݿ. \,HHiv0~#w^IENDB`traits-4.6.0/examples/tutorials/images/parent.png000066400000000000000000000016121300633736300221430ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?% X@#cF%&RullL,ǎ?jb``@1ARL @;/פ09j!r`P@`035e`8-դxgUw>``hmg!K+8X2 @YiU0[1ܹlXV3B  _Ex̚u Ò 8baO |d>َEVAoIX8FtxDZ10Ϗ gC g"tN3N ! f!֕56rlSOմ1|}aѯ ~b&+;p0|AL ~D F۬1Tx1@囟 ?``fϿ1<~ #{W9-8~MmNV6 e5S:w .`/O?3/2裟@@33(1a|!myKϧ3ps Fhn`TݙjQ )3s+Odad7w^?ytwǟ0sa`ef F(.H^scc}ʴݹ}ٖ8e`6b(sFŧPdee`//hf@ X H 0xet`IENDB`traits-4.6.0/examples/tutorials/images/run.png000066400000000000000000000020211300633736300214510ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?% XJK; f100+rrR8e55i+;x~GbIŮ55dcc>~|]RA##T%a* rrꊞ*< L >ccc)w X~Ġ1p -FF`^GG6>>N70^ׯ&߿4$$$W mãG}:ADAZZAHt!!aǏoܸ| @A @3,[wihY\#'' 6`ݺ_ϟg & ر**2.]:=a߾ˁ1.!!Y ӇoY޽{k/_]AFFMCC_'$$AO߿c^F ,-YLÇ>b>u7o^~5+_JIIs XZZ)(H1/2,Z(@xx?VPPa`aaPY hjwA\\AJJ(W _5W qq `%++.Gϟ89ف)ܹ8q`ӧ˯_?,X0@\moQAQQ 8gP`FP͛gW^jy X@ĭ[WWp缼ڥrr߿bsP>0a2<{f2@0 ܹss %(qrr1̜9=z/F=ã  ,-mqssCX=;&cg+IENDB`traits-4.6.0/examples/tutorials/traits_4.0/000077500000000000000000000000001300633736300205665ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/decorators/000077500000000000000000000000001300633736300227335ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/decorators/cached_property.py000066400000000000000000000067671300633736300265000ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(cached_property Decorator)------------------------------------------------- """ cached_property Decorator ========================= New in Traits 3.0 is the *cached_property* method decorator which helps streamline the process of writing properties which cache their current value. Defining properties is a very powerful technique for exposing as traits items whose value depends upon the current state of other object traits. In particular, this can be very useful for creating synthetic traits which are editable or displayable in a traits-based user interface. In some cases however, the cost of computing the current value of a property may be fairly expensive, so it is often a good idea to *cache* the most recently computed value of the property, and return it as the value of the property until one of the traits the property depends upon changes value, at which point the cache should be cleared and the property value recomputed the next time its value is requested. Combined with the **Property** *depends_on* metadata, the *cached_property* decorator greatly simplifies the process of writing a cached property. Take a look at the following code for example:: class TestScores(HasPrivateTraits): scores = List(Int) average = Property(depends_on='scores') @cached_property def _get_average(self): s = self.scores return (float(reduce(lambda n1, n2: n1 + n2, s, 0)) / len(s)) Presumably this is much easier to write and understand that the following equivalent code written without using *depends_on* and *cached_property*:: class TestScores(HasPrivateTraits): scores = List(Int) average = Property def _get_average(self): if self._average is None: s = self.scores self._average = (float(reduce(lambda n1, n2: n1 + n2, s, 0)) / len(s)) return self._average def _scores_changed(self): old, self._average = self._average, None self.trait_property_changed('average', old, self._average) def _scores_items_changed(self): self._scores_changed() The *cached_property* decorator takes no arguments, and should simply be written on the line preceding the property's *getter* method, as shown in the previous example. Use of the *cached_property* decorator also eliminates the need to add *cached = True* metadata to the property declaration, as was previously required when using *depends_on* metadata with a cached property definition. """ from traits.api import * from traitsui.api import * #--[TestScores Class]---------------------------------------------------------- class TestScores(HasPrivateTraits): scores = List(Int) average = Property(depends_on='scores') @cached_property def _get_average(self): print "...computing average:", s = self.scores return (float(reduce(lambda n1, n2: n1 + n2, s, 0)) / len(s)) #--[Example*]------------------------------------------------------------------ # Create a sample TestScores object with some sample scores: test_scores = TestScores(scores=[89, 93, 76, 84, 62, 96, 75, 81, 69, 90]) # Display the average: print 'First average:', test_scores.average print 'Check that again:', test_scores.average # Now add a few more late scores into the mix: test_scores.scores.extend([85, 61, 70]) # And display the new average: print 'Second average:', test_scores.average traits-4.6.0/examples/tutorials/traits_4.0/decorators/on_trait_change.py000066400000000000000000000134111300633736300264310ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(on_trait_change Decorator)------------------------------------------------- """ on_trait_change Decorator ========================= Until Traits 3.0, the only way to define a static trait notification handler as part of a class definition was to define the method following a certain naming convention:: def _name_changed(self, ...): ... or def _name_fired(self, ...): ... where *name* is the name of the trait to which the notification handler method applies. Starting with Traits 3.0, there is now an alternate method for declaring notification handlers using the **on_trait_change** decorator. The syntax for the decorator is:: @on_trait_change('extended_trait_name') def any_method_name(self, ...): ... where *extended_trait_name* is the name of the trait for which the following method is the notification handler, and *any_method_name* is an arbitrary method name, which does not need to follow any particular naming convention. Although the new syntax is more verbose than the original syntax, it has several advantages: - It does not require using special method names. - It allows using the extended trait naming support added to the **on_trait_change** method in Traits 3.0. The last item is especially important since it allows you to statically declare notification handlers for many more cases than were previously possible. For example:: @on_trait_change('foo,bar,baz') def _update(self): ...perform update logic... defines an *_update* method that is called whenever the class's *foo*, *bar* or *baz* traits change. Previously, this would have required writing three separate notification handlers (one each for the *foo*, *bar* and *baz* traits), or adding *event* metadata to each of the trait declarations for *foo*, *bar* and *baz*. Similarly, the previous technique of writing methods such as:: def _bar_changed_for_foo(self, ...): ... which statically defines a notification handler for changes to the *bar* trait of the object's *foo* trait can now be written as:: @on_trait_change('foo.bar') def any_method_name(self, ...): ... Perhaps even more importantly, this technique can be applied in even more complex situations, such as:: @on_trait_change('foo.bar.baz') def any_method_name(self, ...): ... or @on_trait_change('foo.+dirty,foo2.[bar,baz,foogle]') def any_method_name(self, ...): ... The first case is a simple extension of the previous example, while the second is a somewhat far-fetched example which can be interpreted as defining a method that handles: - Changes to any trait on the object's *foo* trait which has *dirty* metadata defined, or - Changes to the *bar*, *baz* or *foogle* trait of the object's *foo2* trait. Note that there is one important semantic difference between writing:: def _name_changed(self, ...): ... and:: @on_trait_change('name') def any_method_name(self, ...): ... While both are recognized as being notification handlers for the object's *name* trait, the interpretation of the argument signature for the first case follows the static trait change handler pattern, while the second case follows the dynamic **on_trait_change** method pattern. While this might seem obvious, given the fact that the decorator is called *on_trait_change*, it is an important enough difference to note explicitly. A Complete Example ------------------ Refer to the code tabs of this lesson for a complete example using the *on_trait_change* decorator. In particular, look at the definition of the *sick_again* method in the **Corporation Class** tab. """ #--------------------------------------------------------------------- from traits.api import * #--[Employee Class]------------------------------------------------------------ class Employee(HasTraits): # The name of the employee: name = Str # The number of sick days they have taken this year: sick_days = Int #--[Department Class]---------------------------------------------------------- class Department(HasTraits): # The name of the department: name = Str # The employees in the department: employees = List(Employee) #--[Corporation Class]--------------------------------------------------------- class Corporation(HasTraits): # The name of the corporation: name = Str # The departments within the corporation: departments = List(Department) # Define a corporate 'whistle blower' method: @on_trait_change('departments:employees.sick_days') def sick_again(self, object, name, old, new): print '%s just took sick day number %d for this year!' % ( object.name, new) #--[Example*]------------------------------------------------------------------ # Create some sample employees: millie = Employee(name='Millie', sick_days=2) ralph = Employee(name='Ralph', sick_days=3) tom = Employee(name='Tom', sick_days=1) slick = Employee(name='Slick', sick_days=16) marcelle = Employee(name='Marcelle', sick_days=7) reggie = Employee(name='Reggie', sick_days=11) dave = Employee(name='Dave', sick_days=0) bob = Employee(name='Bob', sick_days=1) alphonse = Employee(name='Alphonse', sick_days=5) # Create some sample departments: accounting = Department(name='accounting', employees=[millie, ralph, tom]) sales = Department(name='Sales', employees=[slick, marcelle, reggie]) development = Department(name='Development', employees=[dave, bob, alphonse]) # Create a sample corporation: acme = Corporation(name='Acme, Inc.', departments=[accounting, sales, development]) # Now let's try out our 'reporting' system: slick.sick_days += 1 reggie.sick_days += 1 traits-4.6.0/examples/tutorials/traits_4.0/decorators/tutorial.desc000066400000000000000000000001551300633736300254370ustar00rootroot00000000000000New Method Decorators on_trait_change: on_trait_change Decorator cached_property: cached_property Decorator traits-4.6.0/examples/tutorials/traits_4.0/default.css000066400000000000000000000015001300633736300227200ustar00rootroot00000000000000body { background-color: #FFFFFF; } h1 { font-family: Arial; font-size: 14pt; color: #303030; background-color: #FCD062; padding-top: 3px; padding-left:8px; padding-bottom: 3px; padding-right: 8px; } h2 { font-family: Arial; font-size: 12pt; color: #303030; background-color: #FCD062; padding-top: 3px; padding-left:8px; padding-bottom: 3px; padding-right: 8px; } pre { border: 1px solid #A0A0A0; background-color: #FDF7E7; padding: 4px; } dl { border: 1px solid #A0A0A0; background-color: #FDF7E7; padding-top: 4px; padding-bottom: 6px; padding-left: 8px; padding-right: 8px; } dl dl { border: 0px solid #A0A0A0; } dt { font-family: Arial; font-weight: bold; dd { padding-bottom: 10px; } traits-4.6.0/examples/tutorials/traits_4.0/delegation/000077500000000000000000000000001300633736300227015ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/delegation/delegation.py000066400000000000000000000113651300633736300253740ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(Delegation Fixes and Improvements)----------------------------------------- """ Delegation Fixes and Improvements ================================= In previous versions of Traits there were a number of problems (i.e. bugs) in the delegation support that made delegation virtually unusable in some situations. As a result, one of the primary goals of Traits 3.0 was to fix all known problems with delegation, thus allowing it to reclaim its role as one of the five keys pillars of the Traits package (those pillars being *initialization*, *validation*, *notification*, *delegation* and *visualization*). We are happy to report that not only have all known bugs been fixed, but a previously unsupported, though often requested, feature has been added as well. Delegation Event Notification ----------------------------- Previously, many Traits users implicitly assumed that changes made to a *delegatee* trait would generate a change notification on any *delegater* trait (no matter how many such traits there might be). Unfortunately, this was not the case. However, starting with Traits 3.0, this feature has now been implemented. An example of what this means is shown below:: class Parent(HasTraits): first_name = Str last_name = Str class Child(HasTraits): mother = Instance(Parent) father = Instance(Parent) first_name = Str last_name = Delegate('father') In this example, we've created two classes, **Parent** and **Child**, and the value of the **Child** class's *last_name* trait delegates its value to its *father* object's *last_name* trait. Next, we'll set up a simple set of test objects:: mom = Parent(first_name='Julia', last_name='Wilson') dad = Parent(first_name='William', last_name='Chase') son = Child(mother=mom, father=dad, first_name='John') Finally, we'll set up a notification handler on the *son* object's *last_name* trait and then try out various combinations of setting both the *father* and *son* object's *last_name* trait to see in which cases the notification handler is called:: def name_changed(name): print 'Your last name has been changed to %s.' % name # Set up a change notification handler on the son's last name: son.on_trait_change(name_changed, 'last_name') # This should cause the son's last name to change as well: print "Changing dad's last name to Jones." dad.last_name = 'Jones' # This change overrides the father's last name for the son: print "Changing son's last name to Thomas." son.last_name = 'Thomas' # This should no longer have any effect on the son's last name: print "Changing dad's last name to Riley." dad.last_name = 'Riley' # Son decides to revert his name back to his father's name: print "Reverting son's last name." del son.last_name # Now changing the father's name should affect the son again: print "Changing dad's last name to Simmons." dad.last_name = 'Simmons' For the actual results of running this code, refer to the **Output** tab. Note that for each case in which an explicit or implicit change is made to the *son* object's *last_name* trait, a corresponding call is made to the change notification handler. """ # FIXME - this need to be redone without traitsui from traits.api import * #--[Parent Class]-------------------------------------------------------------- class Parent(HasTraits): first_name = Str last_name = Str #--[Child Class]--------------------------------------------------------------- class Child(HasTraits): mother = Instance(Parent) father = Instance(Parent) first_name = Str last_name = Delegate('father') #--[Example*]------------------------------------------------------------------ mom = Parent(first_name='Julia', last_name='Wilson') dad = Parent(first_name='William', last_name='Chase') son = Child(mother=mom, father=dad, first_name='John') def name_changed(name): print 'Your last name has been changed to %s.' % name # Set up a change notification handler on the son's last name: son.on_trait_change(name_changed, 'last_name') # This should cause the son's last name to change as well: print "Changing dad's last name to Jones." dad.last_name = 'Jones' # This change overrides the father's last name for the son: print "Changing son's last name to Thomas." son.last_name = 'Thomas' # This should no longer have any effect on the son's last name: print "Changing dad's last name to Riley." dad.last_name = 'Riley' # Son decides to revert his name back to his father's name: print "Reverting son's last name." del son.last_name # Now changing the father's name should affect the son again: print "Changing dad's last name to Simmons." dad.last_name = 'Simmons' traits-4.6.0/examples/tutorials/traits_4.0/delegation/tutorial.desc000066400000000000000000000000421300633736300254000ustar00rootroot00000000000000Delegation Fixes and Improvements traits-4.6.0/examples/tutorials/traits_4.0/extended_trait_change/000077500000000000000000000000001300633736300250765ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/extended_trait_change/extended_trait_change.py000066400000000000000000000263521300633736300317700ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(on_trait_change Method Enhancements)--------------------------------------- """ on_trait_change Method Enhancements =================================== In Traits 3.0, the capabilities of the **HasTraits** class's *on_trait_change* method has been greatly enhanced with the addition of a new *extended* trait name syntax for specifying the name of the trait the notification handler applies to. Previously, the trait name argument to the *on_trait_change* method could only be one of the following: Omitted, None, or 'anytrait' The notification handler applies to any trait on the object. name The notification handler applies to the trait on the object called *name*. [name1, ..., namen] The notification handler applies to each of the traits on the object with the specified names. In Traits 3.0, all of these forms are still supported, but now the syntax for specifying *name* has been expanded to allow a much broader set of traits that are *reachable* from the object the *on_trait_change* method is applied to. New *name* Parameter Syntax --------------------------- In Traits 3.0, the *name* parameter, in addition to being omitted, None or *anytrait*, can now be a single *xname* or a list of *xname* names, where an *xname* is an extended name of the form:: xname2['.'xname2]* An *xname2* is of the form:: (xname3 | '['xname3[','xname3]*']) ['*'] An *xname3* is of the form:: xname | ['+'|'-'][name] | name['?' | ('+'|'-')[name]] A *name* is any valid Python attribute name. The semantic meaning of this notation is as follows: [item, item, ..., item] A list which matches any of the specified items. Note that at the topmost level, the surrounding square brackets are optional. name? If the current object does not have an attribute called *name*, the reference can be ignored. If the '?' character is omitted, the current object must have a trait called *name*, otherwise an exception will be raised. prefix+ Matches any trait on the current object whose name begins with *prefix*. \+metadata_name Matches any trait on the current object having *metadata_name* metadata. \-metadata_name Matches any trait on the current object which does not have *metadata_name* metadata. prefix+metadata_name Matches any trait on the current object whose name begins with *prefix* and which has *metadata_name* metadata. prefix-metadata_name Matches any trait on the current object whose name begins with *prefix* and which does not have *metadata_name* metadata. \+ Matches all traits on the current object. pattern* Matches object graphs where *pattern* occurs one or more times (useful for setting up listeners on recursive data structures like trees or linked lists). Name Syntax Examples -------------------- Some examples of valid names and their meaning are as follows: 'foo,bar,baz' Listen for trait changes to *object.foo*, *object.bar*, and *object.baz*. ['foo','bar','baz'] Equivalent to 'foo,bar,baz', but may be more useful in cases where the individual items are computed. 'foo.bar.baz' Listen for trait changes to *object.foo.bar.baz*. 'foo.[bar,baz]' Listen for trait changes to *object.foo.bar* and *object.foo.baz*. '([left,right]).name' Listen for trait changes to the *name* trait of each node of a tree having *left* and *right* links to other tree nodes, and where *object* is the root node of the tree. '+dirty' Listen for trait changes on any trait in the *object* which has the 'dirty' metadata set. 'foo.+dirty' Listen for trait changes on any trait in *object.foo* which has the 'dirty' metadata set. 'foo.[bar,-dirty]' Listen for trait changes on *object.foo.bar* or any trait on *object.foo* which does not have 'dirty' metadata set. Additional Semantic Rules ------------------------- Note that any of the intermediate (i.e., non-final) links in a pattern can be traits of type **Instance**, **List** or **Dict**. In the case of **List** and **Dict** traits, the subsequent portion of the pattern is applied to each item in the list, or value in the dictionary. For example, if *self.children* is a list, 'children.name' listens for trait changes to the *name* trait for each item in the *self.children* list. Also note that items added to or removed from a list or dictionary in the pattern will cause the *handler* routine to be invoked as well, since this is treated as an *implied* change to the item's trait being monitored. Notification Handler Signatures ------------------------------- The signature of the *handler* supplied also has an effect on how changes to intermediate traits are processed. The five valid handler signatures are: 1. handler() 2. handler(new) 3. handler(name,new) 4. handler(object,name,new) 5. handler(object,name,old,new) For signatures 1, 4 and 5, any change to any element of a path being listened to invokes the handler with information about the particular element that was modified (e.g., if the item being monitored is 'foo.bar.baz', a change to 'bar' will call *handler* with the following information: object object.foo name bar old old value for object.foo.bar new new value for object.foo.bar If one of the intermediate links is a **List** or **Dict**, the call to *handler* may report an *_items* changed event. If in the previous example, *bar* is a **List**, and a new item is added to *bar*, then the information passed to *handler* would be: object object.foo name bar_items old **Undefined** new **TraitListEvent** whose *added* trait contains the new item added to *bar*. For signatures 2 and 3, the *handler* does not receive enough information to discern between a change to the final trait being listened to and a change to an intermediate link. In this case, the event dispatcher will attempt to map a change to an intermediate link to its effective change on the final trait. This only works if all of the intermediate links are single values (such as an **Instance** or **Any** trait) and not **Lists** or **Dicts**. If the modified intermediate trait or any subsequent intermediate trait preceding the final trait is a **List** or **Dict**, then a **TraitError** is raised, since the effective value for the final trait cannot in general be resolved unambiguously. Handler signature 1 also has the special characteristic that if a final trait is a **List** or **Dict**, it will automatically handle *_items* changed events for the final trait as well. This can be useful in cases where the *handler* only needs to know that some aspect of the final trait has been changed. For all other *handler* signatures, you must explicitly specify the *xxx_items* trait if you want to be notified of changes to any of the items of the *xxx* trait. Backward Compatibility ---------------------- The new extended trait name support in Traits 3.0 has one slight semantic difference with the pre-Traits 3.0 *on_trait_change* method. Prior to Traits 3.0, it was necessary to make two separate calls to *on_trait_change* in order to set up listeners on a **List** or **Dict** trait's value and the contents of its value, as shown in the following example:: class Department(HasTraits): employees = List(Employee) ... a_department.on_trait_change(some_listener, 'employees') a_department.on_trait_change(some_listener_items, 'employees_items') In Traits 3.0, this is still the case if the *some_listener* function has one or more arguments. However, if it has no arguments, the *on_trait_change* method will automatically call the function either when the trait's value or its value's contents change. So in Traits 3.0 it is only necessary to write:: a_department.on_trait_change(some_listener, 'employees') if the *some_listener* (and *some_listener_items*) function has no arguments. The net effect of this difference is that code written prior to Traits 3.0 could set up two listeners (e.g. *some_listener* and *some_listener_items*, as in the example), and then have *both* methods called when the contents of the trait are modified if the *some_listener* method takes no arguments. Since no data is passed to the *some_listener* function, there is probably no harm in doing this, but it does create unnecessary notification handler calls. As a result, to avoid creating this unwanted overhead in existing code, the *on_trait_change* method applies pre-Traits 3.0 semantics to all simple names passed to it (e.g. 'employees'). If you are writing new code and want to take advantage of the new Traits 3.0 *on_trait_change* semantics for a simple trait name, you will need to modify the name to use some recognizable aspect of the new extended trait name syntax. For example, either of the following lines would cause *new style* semantics to be applied to the *employees* trait:: a_department.on_trait_change(some_listener, ' employees') a_department.on_trait_change(some_listener, '[employees]') A Complete Example ------------------ Refer to the code tabs of this lesson for a complete example using *on_trait_change* with an extended trait name. In particular, check near the bottom of the **Example** tab for the code that sets up an extended trait change notification handler using *on_trait_change*. """ from traits.api import * #--[Employee Class]------------------------------------------------------------ class Employee(HasTraits): # The name of the employee: name = Str # The number of sick days they have taken this year: sick_days = Int #--[Department Class]---------------------------------------------------------- class Department(HasTraits): # The name of the department: name = Str # The employees in the department: employees = List(Employee) #--[Corporation Class]--------------------------------------------------------- class Corporation(HasTraits): # The name of the corporation: name = Str # The departments within the corporation: departments = List(Department) #--[Example*]------------------------------------------------------------------ # Create some sample employees: millie = Employee(name='Millie', sick_days=2) ralph = Employee(name='Ralph', sick_days=3) tom = Employee(name='Tom', sick_days=1) slick = Employee(name='Slick', sick_days=16) marcelle = Employee(name='Marcelle', sick_days=7) reggie = Employee(name='Reggie', sick_days=11) dave = Employee(name='Dave', sick_days=0) bob = Employee(name='Bob', sick_days=1) alphonse = Employee(name='Alphonse', sick_days=5) # Create some sample departments: accounting = Department(name='accounting', employees=[millie, ralph, tom]) sales = Department(name='Sales', employees=[slick, marcelle, reggie]) development = Department(name='Development', employees=[dave, bob, alphonse]) # Create a sample corporation: acme = Corporation(name='Acme, Inc.', departments=[accounting, sales, development]) # Define a corporate 'whistle blower' function: def sick_again(object, name, old, new): print '%s just took sick day number %d for this year!' % ( object.name, new) # Set up the function as a listener: acme.on_trait_change(sick_again, 'departments.employees.sick_days') # Now let's try it out: slick.sick_days += 1 reggie.sick_days += 1 traits-4.6.0/examples/tutorials/traits_4.0/extended_trait_change/properties.py000066400000000000000000000164641300633736300276570ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(Extended Property depends_on References)----------------------------------- """ Extended Property *depends_on* References ========================================= In Traits 3.0, the **Property** *depends_on* metadata has been extended to take advantage of the new extended trait name support offered by the *on_trait_change* method. Previously, the *depends_on* metadata for a *Property* was restricted to referencing traits defined either on the same object as the **Property**, or on an object immediately reachable from the object. For example:: class Wheel(Part): axel = Instance(Axel) position = Property(depends_on = 'axel.position') ... Starting with Traits 3.0, the *depends_on* metadata may now include any extended trait reference that is allowed by the enhanced *on_trait_change* method. So, for example it is now legal to write things like:: class Wheel(Part): axel = Instance(Axel) position = Property(depends_on = 'axel.chassis.position') or:: class Child(Person): mother = Instance(Person) father = Instance(Person) mood = Property(depends_on = ['mother.+mood_affecting', 'father.+mood_affecting']) In particular, in the last example we are declaring that the **Child** class's *mood* property depends upon the values of any of either its mother or father object's traits that have *mood_affecting* metadata defined. Thus, a **Child** object's *mood* property will fire a trait change notification whenever any of the its mother's or father's mood affecting traits change. Refer also to the code tabs for this lesson for a complete example using a **Property** definition using *depends_on* metadata containing an extended trait reference. In particular, take a look at the **LeagueModelView Class** tab's *total_hits* trait definition. """ # FIXME redo example without traitsui from traits.api import * from traitsui.api import * from traitsui.table_column import * #--[Player Class]-------------------------------------------------------------- # Define a baseball player: class Player(HasTraits): # The name of the player: name = Str('') # The number of hits the player made this season: hits = Int #--[Team Class]---------------------------------------------------------------- # Define a baseball team: class Team(HasTraits): # The name of the team: name = Str('') # The players on the team: players = List(Player) # The number of players on the team: num_players = Property(depends_on='players') def _get_num_players(self): """ Implementation of the 'num_players' property. """ return len(self.players) #--[League Class]-------------------------------------------------------------- # Define a baseball league model: class League(HasTraits): # The name of the league: name = Str('') # The teams in the league: teams = List(Team) #--[LeagueModelView Class]----------------------------------------------------- # Define a ModelView for a League model: class LeagueModelView(ModelView): # The currently selected team: team = Instance(Team) # The currently selected player: player = Instance(Player) # Button to add a hit to the current player: got_hit = Button('Got a Hit') # The total number of hits (note the 'depends_on' extended trait # reference): total_hits = Property(depends_on='model.teams.players.hits') @cached_property def _get_total_hits(self): """ Returns the total number of hits across all teams and players. """ return reduce(add, [reduce(add, [p.hits for p in t.players], 0) for t in self.model.teams], 0) view = View( VGroup( HGroup( Item('total_hits', style='readonly'), label='League Statistics', show_border=True, ), VGroup( Item('model.teams', show_label=False, editor=TableEditor( columns=[ObjectColumn(name='name', width=0.70), ObjectColumn(name='num_players', label='# Players', editable=False, width=0.29)], selected='object.team', auto_add=True, row_factory=Team, configurable=False, sortable=False) ), label='League Teams', show_border=True, ), VGroup( Item('object.team.players', show_label=False, editor=TableEditor( columns=[ObjectColumn(name='name', width=0.70), ObjectColumn(name='hits', editable=False, width=0.29)], selected='object.player', auto_add=True, row_factory=Player, configurable=False, sortable=False), ), '_', HGroup( Item('got_hit', show_label=False, enabled_when='player is not None', ), ), label='Team Players', show_labels=False, show_border=True, ), ), resizable=True, ) def _model_changed(self, model): """ Handles the 'league' model being initialized. """ if len(model.teams) > 0: self.team = model.teams[0] def _got_hit_changed(self): """ Handles the currently selected player making a hit. """ self.player.hits += 1 def _team_changed(self, team): """ Handles a new team being selected. """ if len(team.players) > 0: self.player = team.players[0] else: self.player = None # Function to add two numbers (used with 'reduce'): add = lambda a, b: a + b #--[Example*]------------------------------------------------------------------ # Define some sample teams and players: blue_birds = Team(name='Blue Birds', players=[ Player(name='Mike Scott', hits=25), Player(name='Willy Shofield', hits=37), Player(name='Tony Barucci', hits=19)]) chicken_hawks = Team(name='Chicken Hawks', players=[ Player(name='Jimmy Domore', hits=34), Player(name='Bill Janks', hits=16), Player(name='Tim Saunders', hits=27)]) eagles = Team(name='Eagles', players=[ Player(name='Joe Peppers', hits=33), Player(name='Sam Alone', hits=12), Player(name='Roger Clemson', hits=23)]) # Create a league and its corresponding model view: demo = LeagueModelView( League(name='National Baseball Conference', teams=[blue_birds, chicken_hawks, eagles])) traits-4.6.0/examples/tutorials/traits_4.0/extended_trait_change/tutorial.desc000066400000000000000000000002451300633736300276020ustar00rootroot00000000000000Extended Trait Change Notification Syntax extended_trait_change: on_trait_change Method Enhancements properties: Extended Property depends_on References traits-4.6.0/examples/tutorials/traits_4.0/getstate_setstate/000077500000000000000000000000001300633736300243225ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/getstate_setstate/getstate.py000066400000000000000000000153521300633736300265220ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(__getstate__/__setstate__ Changes and Improvements)------------------------ """ __getstate__/__setstate__ Changes and Improvements ================================================== Originally, the **HasTraits** class did not define specific *__getstate__* or *__setstate__* methods for dealing with *pickling* and *unpickling* of traits-based objects. However, in the course of developing a number of fairly large-scale applications using Traits, experience has shown that some traits specific support in this area would be of benefit to most application developers. Accordingly, Traits 3.0 introduces *__getstate__* and *__setstate__* methods that implement several traits aware serialization and deserialization policies. The *__getstate__* Method ------------------------- One of the most frequently occurring requirements for serializing an object is the ability to control which parts of the object's state are saved, and which parts are discarded. One typical approach is to define a *__getstate__* method which makes a copy of the object's *__dict__* attribute and deletes those items which should not be saved. While this approach works, there are some drawbacks, especially in cases where heavy use of subclassing is used. The **HasTraits** *__getstate__* method uses a somewhat different approach by providing a generic implementation which implements *policies* that developers can customize through the use of traits *metadata*, in many cases completely eliminating the need to override or define a *__getstate__* method in their application classes. In particular, the **HasTraits** *__getstate__* method saves the value of all traits which do not have *transient = True* metadata defined. This policy allows developers to easily mark which trait values should not be saved simply by adding *transient = True* metadata to them. Besides avoiding having to write a *__getstate__* method for their class, this approach also provides good documentation about the *pickling* behavior of the class. For example:: class DataBase(HasTraits): # The name of the data base file: file_name = File # The open file handle used to access the data base: file = Any(transient = True) In this example, the **DataBase** class's *file* trait has been mark as *transient* because it normally contains an open file handle used to access a data base. Since file handles typically cannot be pickled and restored, the file handle should not be saved as part of the object's persistent state. Normally, the file handle would be re-opened by application code after the object has been restored from its persisted state. Predefined *transient* Traits ----------------------------- The Traits package automatically assigns *transient = True* metadata to a number of predefined traits, thus avoiding the need to explicitly mark them as transient yourself. The predefined traits marked as *transient* are: - **Constant**. - **Event**. - *read-only* or *write-only* **Property** traits. - The *xxx_* trait for *mapped* traits. - All *_xxx* traits for classes that subclass **HasPrivateTraits**. Also, by default, delegated traits are only saved if they have a local value which overrides the value defined by its delegate. You can set *transient = True* on the delegate trait if you do not want its value to ever be saved. Overriding *__getstate__* ------------------------- In general, you should avoid overriding *__getstate__* in subclasses of **HasTraits**. Instead, mark traits that should not be pickled with *transient = True* metadata. However, in cases where this strategy is insufficient, we recommend overriding *__getstate__* using the follow pattern to remove items that should not be persisted:: def __getstate__(self): state = super(XXX, self).__getstate__() for key in [ 'foo', 'bar' ]: if key in state: del state[ key ] return state The *__setstate__* Method ------------------------- The main difference between the default Python *__setstate__*-like behavior and the new **HasTraits** class *__setstate__* method is that the **HasTraits** *__setstate__* method actually *sets* the value of each trait using the values passed to it via its state dictionary argument instead of simply storing or copying the state dictionary to its *__dict__* attribute. While slower, this has the advantage of causing trait change notifications to be generated, which can be very useful for classes which rely of receiving notifications in order to ensure that their internal object state remains consistent and up to date. Overriding *__setstate__* ------------------------- For classes which do not want to receive change notifications during *__setstate__*, it is possible to override *__setstate__* and update the object's *__dict__* attribute directly. However, in such cases it is important to either call the *__setstate__* super method (with an empty state dictionary, for example), or to call the **HasTraits** class's private *_init_trait_listeners* method directly. This method has no arguments and does not return a result, but it must be called during *__setstate__* in order to ensure that all dynamic trait change notifications managed by traits are correctly initialized for the object. Failure to call this method may result in lost change notifications. """ from traits.api import * from time import time, sleep from cPickle import dumps, loads #--[Session Class]------------------------------------------------------------- class Session(HasTraits): # The name of the session: name = Str # The time the session was created: created = Any(transient=True) def _name_changed(self): self.created = time() #--[Example*]------------------------------------------------------------------ # The following shows an example of pickling and unpickling a Session object. # Unfortunately, it is not possible to successfully pickle objects created as # part of a tutorial, because of problems with pickling objects derived from # classes dynamically defined using 'exec'. So just use your imagination on # this one... # Create a new session: session = Session(name='session_1') # Display its contents: print 'Session name:', session.name print 'Session created:', session.created # # Simulate saving the session to a file/database: # saved_session = dumps(session) # # # Simulate the passage of time (zzzzZZZZ...): # sleep(1) # # # Simulate restoring the session from a file/database: # restored_session = loads(saved_session) # # # Display the restored sessions contents (note that the 'created' # # time should be different from the original session): # print 'Restored session name:', restored_session.name # print 'Restored session created:', restored_session.created traits-4.6.0/examples/tutorials/traits_4.0/getstate_setstate/tutorial.desc000066400000000000000000000000421300633736300270210ustar00rootroot00000000000000__getstate__/__setstate__ Changes traits-4.6.0/examples/tutorials/traits_4.0/interfaces/000077500000000000000000000000001300633736300227115ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/interfaces/adaptation.py000066400000000000000000000237531300633736300254210ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(Adaptation)---------------------------------------------------------------- """ Adaptation ========== *Adaptation* is the process of transforming an object that does not implement a specific interface, or set of interfaces, into one that does. Defining Adapters ----------------- In traits, an *adapter* is a special type of class whose role is to *transform* some type of object which does not implement a specific interface, or set of interfaces, into one that does. Traits provides several different ways of writing adapters. We'll begin with the simplest way, which is to create a subclass of **Adapter**. Subclassing Adapter ------------------- The **Adapter** base class is designed specifically for creating adapter classes. It is actually a very simple class which streamlines the process of creating new adapter classes by: - Providing a standard constructor which normally does not need to be overridden by subclasses. - Only requiring use of the **adapts** function to define the adapter. The standard constructor for the **Adapter** class has the form:: adapter_subclass_name(object_to_be_adapted) where *adapter_subclass_name* is the name of the **Adapter** subclass. The only thing the constructor does is:: self.adaptee = object_to_be_adapted which assigns the object being adapted to the *adaptee* trait. As an adapter writer, the only things you need to add to the subclass definition are: - An **adapts** function call declaring which interfaces the adapter class implements on behalf of the object it is adapting. The form of the **adapts** function is as follows:: adapts(client_class, interface [, interface2, ..., interfacen]) - A declaration for the *adaptee* trait (usually as an **Instance** of a particular class). - The actual implementations of the interfaces declared in the **adapts** call. Usually the implementation code will be written in terms of the adapter class's *adaptee* trait assigned by the class constructor. The following shows the definition of a simple adapter class:: from traits.api import Adapter, Instance, implements class PersonINameAdapter (Adapter): # Declare what interfaces this adapter implements for its client: adapts(Person, IName) # Declare the type of client it supports: adaptee = Instance(Person) # Implement the 'IName' interface on behalf of its client: def get_name (self): return ('%s %s' % (self.adaptee.first_name, self.adaptee.last_name)) Rolling You Own Adapter Classes ------------------------------- Note that using the **Adapter** base class is simply a convenience. It is not necessary for an adapter class to derive from **Adapter**. However, if you do not derive your adapter class from **Adapter**, then it is your responsibility to provide all of the same information and setup implicitly provided by **Adapter**. In particular, in addition to using the *adapts* function to declare the set of interfaces the class implements for its client object, you must also define the constructor, or whatever means you define for binding the object to be adapted to the adapter. Creating an adapter class from scratch, we can re-write the previous adapter example as follows:: from traits.api import HasTraits, Instance, adapts class PersonINameAdapter(HasTraits): # Declare what interfaces this adapter implements, and for who: adapts(Person, IName) # Declare the type of client it supports: client = Instance(Person) # Implement the adapter's constructor: def __init__ (self, client): self.client = client # Implement the 'IName' interface on behalf of its client: def get_name (self): return ('%s %s' % (self.client.first_name, self.client.last_name)) As you can see, the main difference between this example and the last is: - Explicit implementation of the adapter constructor. Yet Another Way To Define Adapters ---------------------------------- It is also possible to declare a class to be an adapter class external to the class definition itself, as shown in the following example:: class AnotherPersonAdapter (object): # Implement the adapter's constructor: def __init__ (self, person): self.person = person # Implement the 'IName' interface on behalf of its client: def get_name (self): return ('%s %s' % (self.person.first_name, self.person.last_name)) ... adapts(AnotherPersonAdapter, Person, IName) When used in this way, the form of the **adapts** function is:: adapts(adapter_class, client_class, interface [, interface2, ..., interfacen]) This form simply inserts the adapter class as the first argument (when **adapts** is used inside of a class definition, this information is implicitly available from the class itself). Using Adapters -------------- Now for the good part... how do you use adapters? And the answer is... you don't. At least not explicitly. In traits, adapters are created automatically whenever you assign an object to an *interface* **AdaptsTo** or **AdaptedTo** trait and the object being assigned does not implement the required interface. In this case, if an adapter class exists that can adapt the specified object to the required interface, an instance of the adapter class will be created for the object, and the resulting adapter object is what ends up being assigned to the trait, along with the original object. When using the **AdaptedTo** trait, the adapter is assigned as the value of the trait, and the original object is assigned as its *mapped* value. For the **AdaptsTo** trait, the original object is assigned as the trait value, and the adapter is assigned as its *mapped* value. In the case where the object does not require an adapter, the object and adapted value are the same. Note that it might happen that no adapter class exists that will adapt the object to the required interface, but a pair, or series, of adapter classes exist that will together adapt the object to the needed interface. In this case, the required set of adapters will automatically be created for the object and the final link in the chain adapter object (the one that actually implements the required interface for some object class) will be used. Whenever a situation like this arises, the adapted object used will always contain the smallest set of available adapter objects needed to adapt the original object. The following code shows a simple example of using adaptation:: # Create a Person object (which does not implement the 'IName' interface): william = Person(first_name = 'William', last_name = 'Adams') # Create an apartment, and assign 'renter' the previous object. Since # the value of 'renter' must implement 'IName', a 'PersonINameAdapter' # object is automatically created and assigned: apt = Apartment(renter = william) # Verify that the resulting value implements 'IName' correctly: print 'Renter is: ', apt.renter.get_name() # Check the type of object actually assigned to 'renter': print apt.renter Refer to the **Output** tab for the actual result of running this example. Controlling Adaptation ---------------------- The **AdaptedTo** and **AdaptsTo** traits are actually subclasses of the **Instance** trait. Normally, adaptation occurs automatically when values are assigned to an **AdaptedTo** or **AdaptsTo** trait. However, any of the **Instance**, **AdaptedTo** and **AdaptsTo** traits allow you to control how adaptation is performed by means of the *adapt* metadata, which can have one of the following values: no Adaptation is not allowed (This is the default for the **Instance** trait). yes Adaptation is allowed. If adaptation fails, an exception is raised (This is the default for both the **AdaptedTo** and **AdaptsTo** traits). default Adapation is allowed. If adaptation fails, the default value for the trait is assigned instead. As an example of modifying the adaptation behavior of an **AdaptedTo** trait, we could rewrite the example **Apartment** class as follows:: class Apartment(HasTraits): renter = AdaptedTo(IName, adapt = 'no') Using this definition, any value assigned to *renter* must itself implement the **IName** interface, otherwise an exception is raised. Try modifying and re-running the example code to verify that this is indeed the case. """ #--------------------------------------------------------------------- from traits.api import * #--[IName Interface]----------------------------------------------------------- # Define the 'IName' interface: class IName (Interface): def get_name(self): """ Returns the name of an object. """ #--[Person Class]-------------------------------------------------------------- class Person(HasTraits): first_name = Str('John') last_name = Str('Doe') #--[PersonINameAdapter Class]-------------------------------------------------- class PersonINameAdapter(Adapter): # Declare what interfaces this adapter implements for its client: adapts(Person, IName) # Declare the type of client it supports: adaptee = Instance(Person) # Implementation of the 'IName' interface on behalf of its client: def get_name(self): """ Returns the name of an object. """ return ('%s %s' % (self.adaptee.first_name, self.adaptee.last_name)) #--[Apartment Class]----------------------------------------------------------- # Define a class using an object that implements the 'IName' interface: class Apartment(HasTraits): renter = AdaptedTo(IName) #--[Example*]------------------------------------------------------------------ # Create an object implementing the 'IName' interface: william = Person(first_name='William', last_name='Adams') # Create an apartment, and assign 'renter' an object implementing 'IName': apt = Apartment(renter=william) # Verify that the object works correctly: print 'Renter is:', apt.renter.get_name() # Check the type of object actually assigned to 'renter': print apt.renter traits-4.6.0/examples/tutorials/traits_4.0/interfaces/interfaces.py000066400000000000000000000101601300633736300254040ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(Interfaces)---------------------------------------------------------------- """ Interfaces ========== In Traits 3.0, the ability to define, implement and use *interfaces* has been added to the package. Defining Interfaces ------------------- Interfaces are defined by subclassing from the **Interface** class, as shown in the example below:: from traits.api import Interface class IName(Interface): def get_name(self): " Returns the name of an object. " This same code is shown in the **IName Interface** tab of the code. Interface classes are intended mainly as documentation of the methods and traits that the interface defines, and should not contain any actual implementation code, although no check is performed to enforce this currently. Implementing Interfaces ----------------------- A class declares that it implements one or more interfaces using the **implements** function, which has the form:: implements(interface [, interface2, ..., interfacen]) The semantics of this function is that the class declares that it implements each of the *interfaces* specified as an argument to **implements**. Also, the call to **implements** must occur at class scope within the class definition, as shown in the following example:: from traits.api import HasTraits, implements class Person(HasTraits): implements(IName) ... Only a single call to **implements** should occur within a class definition. Refer to the **Person Class** tab in the code for a complete example of using **implements**. Note that in the current version, traits does not check to ensure that the class containing the **implements** function actually implements the interfaces it says it does. Using Interfaces ---------------- Being able to define and implement interfaces would be of little use without the ability to *use* interfaces in your code. In traits, using an interface is accomplished using the **Instance** trait, as shown in the following example:: from traits.api import HasTraits, Instance class Apartment(HasTraits): renter = Instance(IName) Using an interface class in an **Instance** trait definition declares that the trait only accepts values which are objects that either: - Implement the specified interface. - Can be adapted to an object that implements the specified interface. Additional information on what it means to *adapt* an object to implement an interface is presented in the next section of the tutorial. As before, the **Instance** trait can also be used with classes that are not interfaces, such as:: from traits.api import HasTraits, Instance class Apartment(HasTraits): renter = Instance(Person) In this case, the value of the trait must be an object which is an instance of the specified class or one of its subclasses. """ #--------------------------------------------------------------------- from traits.api import * #--[IName Interface]----------------------------------------------------------- # Define the 'IName' interface: class IName(Interface): def get_name(self): """ Returns the name of an object. """ #--[Person Class]-------------------------------------------------------------- class Person(HasTraits): implements(IName) first_name = Str('John') last_name = Str('Doe') # Implementation of the 'IName' interface: def get_name(self): """ Returns the name of an object. """ return ('%s %s' % (self.first_name, self.last_name)) #--[Apartment Class]----------------------------------------------------------- # Define a class using an object that implements the 'IName' interface: class Apartment(HasTraits): renter = Instance(IName) #--[Example*]------------------------------------------------------------------ # Create an object implementing the 'IName' interface: william = Person(first_name='William', last_name='Adams') # Create an apartment, and assign 'renter' an object implementing 'IName': apt = Apartment(renter=william) # Verify that the object works correctly: print 'Renter is:', apt.renter.get_name() traits-4.6.0/examples/tutorials/traits_4.0/interfaces/tutorial.desc000066400000000000000000000001111300633736300254050ustar00rootroot00000000000000Interfaces and Adaptation interfaces: Interfaces adaptation: Adaptation traits-4.6.0/examples/tutorials/traits_4.0/trait_types/000077500000000000000000000000001300633736300231355ustar00rootroot00000000000000traits-4.6.0/examples/tutorials/traits_4.0/trait_types/core_traits.py000066400000000000000000000016211300633736300260250ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(Rewritten Core Traits)----------------------------------------------------- """ Rewritten Core Traits ===================== For several reasons, including the ability to subclass types, many of the previous core Traits package types have been entirely rewritten as subclasses of **TraitType**, the new base class for subclassable trait types. The core trait types which have been rewritten as subclasses of **TraitType** are: - Any - Bool - CBool - CComplex - CInt - CFloat - CLong - Code - Complex - CStr - CUnicode - Dict - Directory - Enum - Expression - File - Float - HTML - Instance - Int - List - Long - Password - PythonValue - Range - Regex - Str - String - Tuple - Unicode - WeakRef This may be useful information if you find yourself in need of creating a new trait type with behavior similar to any of these core trait types. """ traits-4.6.0/examples/tutorials/traits_4.0/trait_types/new_types.py000066400000000000000000000177701300633736300255400ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(Creating New Trait Types)-------------------------------------------------- """ Creating New Trait Types ======================== You create a *new style* trait type by subclassing the **TraitType** class or one of its subclasses, such as **Float** or **Str**. **TraitType** provides the infrastructure for creating a trait type and allows subclasses to define specific methods and class constants used to create either a new trait *type* or *property*. In the next section, we'll cover the methods and class constants used to define a new trait *type*, and in the section following that we'll show how to define a new trait *property*. Defining a New Trait Type ------------------------- The thing that distinguishes a trait *type* from a *property* is the existence of a *validate* method in the subclass. The *validate* method should have the following signature: validate(self, object, name, value) This method validates, coerces, or adapts the specified *value* as the value of the *name* trait of the *object* object. This method is called when a value is assigned to an object trait that is based on this subclass of *TraitType* and the class does not contain a definition for either the *get()* or *set()* methods. The method must return the original *value* or any suitably coerced or adapted value that is a legal value for the trait. If *value* is not a legal value for the trait, and cannot be coerced or adapted to a legal value, the method should either raise a **TraitError** or call the *error()* method to raise a **TraitError** on its behalf. In addition to *validate*, the subclass can also define the *post_setattr* method, which should have the following signature: post_setattr(self, object, name, value) This method allows the trait to do additional processing after *value* has been successfully assigned to the *name* trait of the *object* object. For most traits there is no additional processing that needs to be done, and this method need not be defined. It is normally used for creating *shadow* (i.e., *mapped* traits), but other uses may arise as well. This method does not need to return a value, and should normally not raise any exceptions. The subclass can also define a constant default value by setting the class- level *default_value* attribute to the desired constant value. For example:: class OddInt(Int): default_value = 1 ... If a non-constant default value is desired, you should override the **TraitType** class's *get_default_value* method. Refer to the documentation for the **TraitType** class for more information on what this method does. If you have a constant string that can be used as the type's *info* value, you can provide it by simple setting the string as the value of the class-level *info_text* attribute:: class OddInt(Int): info_text = 'an odd integer' ... If you have a type info string which depends upon the internal state of the trait, then you should override the **TraitType's** *info* method. This method has no arguments, and should return a string describing the values accepted by the trait type (e.g. 'an integer in the range from 1 to 5'). If you also have some type specific initialization that needs to be performed when the trait type is created, you can also override the **TraitType** class's *init* method. This method has no arguments and is automatically called from the **TraitType** class constructor. If you would like to specify a default Traits UI editor for your new trait type, you can also override the **TraitType** class's *create_editor* method, which has no arguments and should return the default **TraitEditor** for any instances of the type to use. This provides a basic overview of the basic methods and class constants needed to define a new trait type. Refer to the complete documentation for the **TraitType** and **BaseTraitHandler** classes for more information on other methods that can be overridden if necessary. Defining a New Trait Property ----------------------------- You can also define new trait *properties* by subclassing from **TraitType** or one of its subclasses. A *property* is distinguished from a *type* by the existence of a *get* and/or *set* method in the **TraitType** subclass. The signature for these two methods is as follows: get(self, object, name) This is the *getter* method of a trait that behaves like a property. It has the following arguments: object The object that the property applies to. name The name of the *object* property. If this method is not defined, but the *set* method is defined, the trait behaves like a *write-only* property. This method should return the value of the *name* property for the *object* object. set(self, object, name, value) This is the *setter* method of a trait that behaves like a property. It has the following arguments: object The object the property applies to. name The name of the property on *object*. value The value being assigned as the value of the property. If this method is not defined, but the *get* method is defined, the trait behaves like a *read-only* property. This method does not need to return a value, but it should raise a **TraitError** exception if the specified *value* is not valid and cannot be coerced or adapted to a valid value. Because the value of a *property* is determined by the *get* method, the *default_value* class constant and *get_default_value* method are not used. However, all other values and methods, such as the *info_text* class attribute and *info* method apply to a *property* as well as a normal type. Please refer to the preceding section on defining a trait type for additional information that applies to properties as well. """ #--------------------------------------------------------------------- from traits.api import * #--[DiceRoll Type]------------------------------------------------------------- # Define a type whose value represents the roll of a pair of dice: class DiceRoll(TraitType): # Set default value to 'snake-eyes': default_value = (1, 1) # Describe the type: info_text = ('a tuple of the form (n,m), where both n and m are integers ' 'in the range from 1 to 6 representing a roll of a pair of ' 'dice') # Validate any value assigned to the trait to make sure it is a valid # dice roll: def validate(self, object, name, value): if (isinstance(value, tuple) and (len(value) == 2) and (1 <= value[0] <= 6) and (1 <= value[1] <= 6)): return value self.error(object, name, value) #--[RandInt Property]---------------------------------------------------------- from random import randint # Define a read-only property whose value is a random integer in a specified # range: class RandInt(TraitType): # Define the type's constructor: def __init__(self, low=1, high=10, **metadata): super(RandInt, self).__init__(**metadata) self.low = int(low) self.high = int(high) # Define the property's getter: def get(self): return randint(self.low, self.high) # Define the type's type information: def info(self): return ('a random integer in the range from %d to %d' % (self.low, self.high)) #--[Craps Class]--------------------------------------------------------------- # Define a test class containing both new trait types/properties: class Craps(HasTraits): rolls = List(DiceRoll) die = RandInt(1, 6) #--[Example*]------------------------------------------------------------------ # Create a test object: craps = Craps() # Add a number of test dice rolls: for i in range(10): craps.rolls.append((craps.die, craps.die)) # Display the results: print craps.rolls # Try to assign an invalid dice roll: try: craps.rolls.append((0, 0)) except TraitError: print 'Assigning an invalid dice roll failed.' traits-4.6.0/examples/tutorials/traits_4.0/trait_types/trait_types.py000066400000000000000000000110261300633736300260560ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # License: BSD Style. #--(New Trait Definition Style)------------------------------------------------ """ New Trait Definition Style ========================== The Traits package comes with a number of predefined traits, such as **Str**, **Int**, **Float**, **Range** and so on. While these core traits suffice for most common programming situations, quite often the need arises to create a new *custom* trait. Traits has always supported creating new traits, but in the past this has typically involved creating a new **TraitHandler** subclass and invoking the **Trait** function to define a new trait based on that subclass, as shown in the following example:: class OddIntHandler(TraitHandler): def validate(self, object, name, value): if isinstance(value, int) and ((value % 2) == 1): return value self.error(object, name, value) def info(self): return 'an odd integer' OddInt = Trait(1, OddIntHandler) OddInt = TraitFactory(OddInt) While not overly complex, nevertheless several developers have complained that that: - The process of creating a new trait is not overly intuitive. - The resulting trait cannot be subclassed to derive a new trait with slightly different behavior. As a result, in Traits 3.0 a new method of defining traits has been added that hopefully addresses both of these issues. Note that this new style of creating traits does not replace the old style of creating traits, but is simply a new technique that can be used instead of the original method. Both old and new style traits can be defined, used and interoperate in the same program without any adverse side effects. OddInt Redux ------------ Using the new style of defining traits, we can rewrite our previous **OddInt** example as follows:: class OddInt(Int): # Define the default value: default_value = 1 # Describe the trait type: info_text = 'an odd integer' def validate(self, object, name, value): value = super(OddInt, self).validate(object, name, value) if (value % 2) == 1: return value self.error(object, name, value) This provides the exact same functionality as the previous definition of **OddInt**. There are several points to make about the new definition however: - The **OddInt** class derives from **Int** (not **TraitHandler**). This has several important side effects: * **OddInt** can re-use and change any part of the **Int** class behavior that it needs to. Note in this case the re-use of the **Int** class's *validate* method via the *super* call in **OddInt's** *validate* method. * As a subclass of **Int**, it is related to **Int**, which can be important both from a documentation and programming point of view. The original definition of **OddInt** was related to **Int** only in that their names were similar. - The default value and trait description information are declared as class constants. Although there are more dynamic techniques that allow computing these values (which will be described in another tutorials), this provides a very simple means of defining these values. - No use of **TraitHandler**, **Trait** or **TraitFactory** is required, just good old OO programming techniques. Hopefully this will make the process of creating a new trait type a little more understandable to a wider group of developers. """ #--------------------------------------------------------------------- from traits.api import * #--[OddInt Definition]--------------------------------------------------------- class OddInt(Int): # Define the default value: default_value = 1 # Describe the trait type: info_text = 'an odd integer' def validate(self, object, name, value): value = super(OddInt, self).validate(object, name, value) if (value % 2) == 1: return value self.error(object, name, value) #--[Test Class]---------------------------------------------------------------- class Test(HasTraits): any_int = Int odd_int = OddInt #--[Example*]------------------------------------------------------------------ # Create a test object: t = Test() # Set both traits to an odd integer value: t.any_int = 1 print "t.any_int:", t.any_int t.odd_int = 1 print "t.odd_int:", t.odd_int # Now set them both to an even value (and see what happens): t.any_int = 2 print "t.any_int:", t.any_int t.odd_int = 2 print "t.odd_int:", t.odd_int # Should never get here! traits-4.6.0/examples/tutorials/traits_4.0/trait_types/tutorial.desc000066400000000000000000000001761300633736300256440ustar00rootroot00000000000000Trait Types trait_types: New Trait Definition Style core_traits: Rewritten Core Traits new_types: Creating New Trait Types traits-4.6.0/examples/tutorials/traits_4.0/tutorial.desc000066400000000000000000000001671300633736300232750ustar00rootroot00000000000000Changes starting with Traits 3.0 interfaces trait_types extended_trait_change decorators delegation getstate_setstate traits-4.6.0/examples/tutorials/tutor.py000066400000000000000000001444251300633736300204400ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Author: David C. Morrill # Date: 03/30/2007 # # fixme: # - Get custom tree view images. # - Write a program to create a directory structure from a lesson plan file. """ A framework for creating interactive Python tutorials. """ # Imports: import sys import os import re from string \ import capwords from traits.api import (HasPrivateTraits, HasTraits, File, Directory, Instance, Int, Str, List, Bool, Dict, Property, Button, cached_property) from traitsui.api import (View, VGroup, HGroup, VSplit, HSplit, Tabbed, Item, Heading, Handler, ListEditor, CodeEditor, HTMLEditor, TreeEditor, TitleEditor, ValueEditor, ShellEditor) from traitsui.menu import NoButtons from traitsui.tree_node import TreeNode from pyface.image_resource import ImageResource try: from traitsui.wx.extra.windows.ie_html_editor \ import IEHTMLEditor from traitsui.wx.extra.windows.flash_editor \ import FlashEditor except: IEHTMLEditor = FlashEditor = None # Constants: # Correct program usage information: Usage = """ Correct usage is: tutor.py [root_dir] where: root_dir = Path to root of the tutorial tree If omitted, 'root_dir' defaults to the current directory.""" # The standard list editor used: list_editor = ListEditor( use_notebook=True, deletable=False, page_name='.title', export='DockWindowShell', dock_style='fixed', ) # The standard code snippet editor used: snippet_editor = ListEditor( use_notebook=True, deletable=False, page_name='.title', export='DockWindowShell', dock_style='tab', selected='snippet', ) # Regular expressions used to match section directories: dir_pat1 = re.compile(r'^(\d\d\d\d)_(.*)$') dir_pat2 = re.compile(r'^(.*)_(\d+\.\d+)$') # Regular expression used to match section header in a Python source file: section_pat1 = re.compile(r'^#-*\[(.*)\]') # Normal section_pat2 = re.compile(r'^#-*<(.*)>') # Hidden section_pat3 = re.compile(r'^#-*\((.*)\)') # Description # Regular expression used to extract item titles from URLs: url_pat1 = re.compile(r'^(.*)\[(.*)\](.*)$') # Normal # Is this running on the Windows platform? is_windows = (sys.platform in ('win32', 'win64')) # Python file section types: IsCode = 0 IsHiddenCode = 1 IsDescription = 2 # HTML template for a default lecture: DefaultLecture = """

    This section contains the following topics:

      %s
    """ # HTML template for displaying a .wmv/.avi movie file: WMVMovieTemplate = """

    """ # HTML template for displaying a QuickTime.mov movie file: QTMovieTemplate = """

    """ # HTML template for displaying an image file: ImageTemplate = """ """ # HTML template for playing an MP3 audio file: MP3Template = """

     

    """ # Returns the contents of a specified text file (or None): def read_file(path, mode='rb'): """ Returns the contents of a specified text file (or None). """ fh = result = None try: fh = file(path, mode) result = fh.read() except: pass if fh is not None: try: fh.close() except: pass return result # Creates a title from a specified string: def title_for(title): """ Creates a title from a specified string. """ return capwords(title.replace('_', ' ')) # Returns a relative CSS style sheet path for a specified path and parent # section: def css_path_for(path, parent): """ Returns a relative CSS style sheet path for a specified path and parent section. """ if os.path.isfile(os.path.join(path, 'default.css')): return 'default.css' if parent is not None: result = parent.css_path if result != '': if path != parent.path: result = os.path.join('..', result) return result return '' # 'StdOut' class: class StdOut(object): """ Simulate stdout, but redirect the output to the 'output' string supplied by some 'owner' object. """ def __init__(self, owner): self.owner = owner def write(self, data): """ Adds the specified data to the output log. """ self.owner.output += data def flush(self): """ Flushes all current data to the output log. """ pass # 'NoDemo' class: class NoDemo(HasPrivateTraits): view = View( Heading('No demo defined for this lab.'), resizable=True ) # 'DemoPane' class: class DemoPane(HasPrivateTraits): """ Displays the contents of a Python lab's *demo* value. """ demo = Instance(HasTraits, factory=NoDemo) view = View( Item('demo', id='demo', show_label=False, style='custom', resizable=True ), id='enthought.tutor.demo', resizable=True ) # 'ATutorialItem' class: class ATutorialItem(HasPrivateTraits): """ Defines the abstract base class for each type of item (HTML, Flash, text, code) displayed within the tutor. """ # The title for the item: title = Str # The path to the item: path = File # The displayable content for the item: content = Property # 'ADescriptionItem' class: class ADescriptionItem(ATutorialItem): """ Defines a common base class for all description items. """ def _path_changed(self, path): """ Sets the title for the item based on the item's path name. """ self.title = title_for(os.path.splitext(os.path.basename( path))[0]) # 'HTMLItem' class: class HTMLItem(ADescriptionItem): """ Defines a class used for displaying a single HTML page within the tutor using the default Traits HTML editor. """ url = Str view = View( Item('content', style='readonly', show_label=False, editor=HTMLEditor() ) ) def _url_changed(self, url): """ Sets the item title when the 'url' is changed. """ match = url_pat1.match(url) if match is not None: title = match.group(2).strip() else: title = url.strip() col = title.rfind('/') if col >= 0: title = os.path.splitext(title[col + 1:])[0] self.title = title @cached_property def _get_content(self): """ Returns the item content. """ url = self.url if url != '': match = url_pat1.match(url) if match is not None: url = match.group(1) + match.group(3) return url return read_file(self.path) def _set_content(self, content): """ Sets the item content. """ self._content = content # 'HTMLStrItem' class: class HTMLStrItem(HTMLItem): """ Defines a class used for displaying a single HTML text string within the tutor using the default Traits HTML editor. """ # Make the content a real trait rather than a property: content = Str # 'IEHTMLItem' class: class IEHTMLItem(HTMLItem): """ Defines a class used for displaying a single HTML page within the tutor using the Traits Internet Explorer HTML editor. """ view = View( Item('content', style='readonly', show_label=False, editor=IEHTMLEditor(), ) ) # 'IEHTMLStrItem' class: class IEHTMLStrItem(IEHTMLItem): """ Defines a class used for displaying a single HTML text string within the tutor using the Traits Internet Explorer HTML editor. """ # Make the content a real trait rather than a property: content = Str # 'FlashItem' class: class FlashItem(HTMLItem): """ Defines a class used for displaying a Flash-based animation or video within the tutor. """ view = View( Item('content', style='readonly', show_label=False, editor=FlashEditor(), ) ) # 'TextItem' class: class TextItem(ADescriptionItem): """ Defines a class used for displaying a text file within the tutor. """ view = View( Item('content', style='readonly', show_label=False, editor=CodeEditor(show_line_numbers=False, selected_color=0xFFFFFF) ) ) @cached_property def _get_content(self): """ Returns the item content. """ return read_file(self.path) # 'TextStrItem' class: class TextStrItem(TextItem): """ Defines a class used for displaying a text file within the tutor. """ # Make the content a real trait, rather than a property: content = Str # 'CodeItem' class: class CodeItem(ATutorialItem): """ Defines a class used for displaying a Python source code fragment within the tutor. """ # The displayable content for the item (override): content = Str # The starting line of the code snippet within the original file: start_line = Int # The currently selected line: selected_line = Int # Should this section normally be hidden? hidden = Bool view = View( Item('content', style='custom', show_label=False, editor=CodeEditor(selected_line='selected_line') ) ) # 'ASection' abstract base class: class ASection(HasPrivateTraits): """ Defines an abstract base class for a single section of a tutorial. """ # The title of the section: title = Str # The path to this section: path = Directory # The parent section of this section (if any): parent = Instance('ASection') # Optional table of contents (can be used to define/locate the # subsections): toc = List(Str) # The path to the CSS style sheet to use for this section: css_path = Property # The list of subsections contained in this section: subsections = Property # List(ASection) # This section can be executed: is_runnable = Bool(True) # Should the Python code be automatically executed on start-up? auto_run = Bool(False) @cached_property def _get_subsections(self): """ Returns the subsections for this section: """ if len(self.toc) > 0: self._load_toc() else: self._load_dirs() # Return the cached list of sections: return self._subsections @cached_property def _get_css_path(self): """ Returns the path to the CSS style sheet for this section. """ return css_path_for(self.path, self.parent) def _load_dirs(self): """ Defines the section's subsections by analyzing all of the section's sub-directories. """ # No value cached yet: dirs = [] path = self.path # Find every sub-directory whose name begins with a number of the # form ddd, or ends with a number of the form _ddd.ddd (used for # sorting them into the correct presentation order): for name in os.listdir(path): dir = os.path.join(path, name) if os.path.isdir(dir): match = dir_pat1.match(name) if match is not None: dirs.append((float(match.group(1)), match.group(2), dir)) else: match = dir_pat2.match(name) if match is not None: dirs.append((float(match.group(2)), match.group(1), dir)) # Sort the directories by their index value: dirs.sort(lambda l, r: cmp(l[0], r[0])) # Create the appropriate type of section for each valid directory: self._subsections = [ sf.section for sf in [ SectionFactory(title=title_for(title), parent=self).trait_set(path=dir) for index, title, dir in dirs ] if sf.section is not None ] def _load_toc(self): """ Defines the section's subsections by finding matches for the items defined in the section's table of contents. """ toc = self.toc base_names = [item.split(':', 1)[0] for item in toc] subsections = [None] * len(base_names) path = self.path # Classify all file names that match a base name in the table of # contents: for name in os.listdir(path): try: base_name = os.path.splitext(os.path.basename(name))[0] index = base_names.index(base_name) if subsections[index] is None: subsections[index] = [] subsections[index].append(name) except: pass # Try to convert each group of names into a section: for i, names in enumerate(subsections): # Only process items for which we found at least one matching file # name: if names is not None: # Get the title for the section from its table of contents # entry: parts = toc[i].split(':', 1) if len(parts) == 1: title = title_for(parts[0].strip()) else: title = parts[1].strip() # Handle an item with one file which is a directory as a normal # section: if len(names) == 1: dir = os.path.join(path, names[0]) if os.path.isdir(dir): section = SectionFactory(title=title, parent=self) subsections[i] = section.trait_set(path=dir).section continue # Otherwise, create a section from the list of matching files: subsections[i] = SectionFactory(title=title, parent=self, files=names ).trait_set(path=path).section # Set the subsections to the non-None values that are left: self._subsections = [subsection for subsection in subsections if subsection is not None] # 'Lecture' class: class Lecture(ASection): """ Defines a lecture, which is a section of a tutorial with descriptive information, but no associated Python code. Can be used to provide course overviews, introductory sections, or lead-ins to follow-on lessons or labs. """ # The list of descriptive items for the lecture: descriptions = List(ATutorialItem) # This section can be executed (override): is_runnable = False view = View( Item('descriptions', style='custom', show_label=False, editor=list_editor ), id='enthought.tutor.lecture' ) # 'LabHandler' class: class LabHandler(Handler): """ Defines the controller functions for the Lab view. """ def init(self, info): """ Handles initialization of the view. """ # Run the associated Python code if the 'auto-run' feature is enabled: if info.object.auto_run: info.object.run_code() # 'Lab' class: class Lab(ASection): """ Defines a lab, which is a section of a tutorial with only Python code. This type of section might typically follow a lecture which introduced the code being worked on in the lab. """ # The set-up code (if any) for the lab: setup = Instance(CodeItem) # The list of code items for the lab: snippets = List(CodeItem) # The list of visible code items for the lab: visible_snippets = Property(depends_on='visible', cached=True) # The currently selected snippet: snippet = Instance(CodeItem) # Should normally hidden code items be shown? visible = Bool(False) # The dictionary containing the items from the Python code execution: values = Dict # The run Python code button: run = Button(image=ImageResource('run'), height_padding=1) # User error message: message = Str # The output produced while the program is running: output = Str # The current demo pane (if any): demo = Instance(DemoPane, ()) view = View( VSplit( VGroup( Item('visible_snippets', style='custom', show_label=False, editor=snippet_editor ), HGroup( Item('run', style='custom', show_label=False, tooltip='Run the Python code' ), '_', Item('message', springy=True, show_label=False, editor=TitleEditor() ), '_', Item('visible', label='View hidden sections' ), ), ), Tabbed( Item('values', id='values_1', label='Shell', editor=ShellEditor(share=True), dock='tab', export='DockWindowShell', ), Item('values', id='values_2', editor=ValueEditor(), dock='tab', export='DockWindowShell', ), Item('output', style='readonly', editor=CodeEditor(show_line_numbers=False, selected_color=0xFFFFFF), dock='tab', export='DockWindowShell', ), Item('demo', id='demo', style='custom', resizable=True, dock='tab', export='DockWindowShell', ), show_labels=False, ), id='splitter', ), id='enthought.tutor.lab', handler=LabHandler ) def _run_changed(self): """ Runs the current set of snippet code. """ self.run_code() @cached_property def _get_visible_snippets(self): """ Returns the list of code items that are currently visible. """ if self.visible: return self.snippets return [snippet for snippet in self.snippets if (not snippet.hidden)] def run_code(self): """ Runs all of the code snippets associated with the section. """ # Reconstruct the lab code from the current set of code snippets: start_line = 1 module = '' for snippet in self.snippets: snippet.start_line = start_line module = '%s\n\n%s' % (module, snippet.content) start_line += (snippet.content.count('\n') + 2) # Reset any syntax error and message log values: self.message = self.output = '' # Redirect standard out and error to the message log: stdout, stderr = sys.stdout, sys.stderr sys.stdout = sys.stderr = StdOut(self) try: try: # Get the execution context dictionary: values = self.values # Clear out any special variables defined by the last run: for name in ('demo', 'popup'): if isinstance(values.get(name), HasTraits): del values[name] # Execute the current lab code: exec module[2:] in values, values # fixme: Hack trying to update the Traits UI view of the dict. self.values = {} self.values = values # Handle a 'demo' value being defined: demo = values.get('demo') if not isinstance(demo, HasTraits): demo = NoDemo() self.demo.demo = demo # Handle a 'popup' value being defined: popup = values.get('popup') if isinstance(popup, HasTraits): popup.edit_traits(kind='livemodal') except SyntaxError, excp: # Convert the line number of the syntax error from one in the # composite module to one in the appropriate code snippet: line = excp.lineno if line is not None: snippet = self.snippets[0] for s in self.snippets: if s.start_line > line: break snippet = s line -= (snippet.start_line - 1) # Highlight the line in error: snippet.selected_line = line # Select the correct code snippet: self.snippet = snippet # Display the syntax error message: self.message = '%s in column %s of line %s' % ( excp.msg.capitalize(), excp.offset, line) else: # Display the syntax error message without line # info: self.message = excp.msg.capitalize() except: import traceback traceback.print_exc() finally: # Restore standard out and error to their original values: sys.stdout, sys.stderr = stdout, stderr # 'Lesson' class: class Lesson(Lab): """ Defines a lesson, which is a section of a tutorial with both descriptive information and associated Python code. """ # The list of descriptive items for the lesson: descriptions = List(ATutorialItem) view = View( HSplit( Item('descriptions', label='Lesson', style='custom', show_label=False, dock='horizontal', editor=list_editor, ), VSplit( VGroup( Item('visible_snippets', style='custom', show_label=False, editor=snippet_editor, ), HGroup( Item('run', style='custom', show_label=False, tooltip='Run the Python code', ), '_', Item('message', springy=True, show_label=False, editor=TitleEditor(), ), '_', Item('visible', label='View hidden sections', ), ), label='Lab', dock='horizontal', ), Tabbed( Item('values', id='values_1', label='Shell', editor=ShellEditor(share=True), dock='tab', export='DockWindowShell', ), Item('values', id='values_2', editor=ValueEditor(), dock='tab', export='DockWindowShell', ), Item('output', style='readonly', editor=CodeEditor(show_line_numbers=False, selected_color=0xFFFFFF), dock='tab', export='DockWindowShell', ), Item('demo', id='demo', style='custom', resizable=True, dock='tab', export='DockWindowShell', ), show_labels=False, ), label='Lab', dock='horizontal', ), id='splitter', ), id='enthought.tutor.lesson', handler=LabHandler, ) # 'Demo' class: class Demo(Lesson): """ Defines a demo, which is a section of a tutorial with both descriptive information and associated Python code which is executed but not shown. """ view = View( HSplit( Item('descriptions', label='Lesson', style='custom', show_label=False, dock='horizontal', editor=list_editor, ), Item('demo', id='demo', style='custom', show_label=False, resizable=True, dock='horizontal', export='DockWindowShell', ), id='splitter', ), id='enthought.tutor.demo', handler=LabHandler, ) # 'SectionFactory' class: class SectionFactory(HasPrivateTraits): """ Defines a class that creates Lecture, Lesson or Lab sections (or None), based on the content of a specified directory. None is returned if the directory does not contain any recognized files. """ # The path the section is to be created for: path = Directory # The list of files contained in the section: files = List(Str) # The parent of the section being created: parent = Instance(ASection) # The section created from the path: section = Instance(ASection) # The title for the section: title = Str # The optional table of contents for the section: toc = List(Str) # The list of descriptive items for the section: descriptions = List(ADescriptionItem) # The list of code snippet items for the section: snippets = List(CodeItem) # The path to the CSS style sheet for the section: css_path = Property # Should the Python code be automatically executed on start-up? auto_run = Bool(False) def _path_changed(self, path): """ Creates the appropriate section based on the value of the path. """ # Get the list of files to process: files = self.files if len(files) == 0: # If none were specified, then use all files in the directory: files = os.listdir(path) # Process the description file (if any) first: for name in files: if os.path.splitext(name)[1] == '.desc': self._add_desc_item(os.path.join(path, name)) break # Try to convert each file into one or more 'xxxItem' objects: toc = [item.split(':', 1)[0].strip() for item in self.toc] for name in files: file_name = os.path.join(path, name) # Only process the ones that are actual files: if os.path.isfile(file_name): # Use the file extension to determine the file's type: root, ext = os.path.splitext(name) if (root not in toc) and (len(ext) > 1): # If we have a handler for the file type, invoke it: method = getattr(self, '_add_%s_item' % ext[1:].lower(), None) if method is not None: method(file_name) # Based on the type of items created (if any), create the corresponding # type of section: if len(self.descriptions) > 0: if len(self.snippets) > 0: if len([snippet for snippet in self.snippets if (not snippet.hidden)]) > 0: self.section = Lesson( title=self.title, path=path, toc=self.toc, parent=self.parent, descriptions=self.descriptions, snippets=self.snippets, auto_run=self.auto_run, ) else: self.section = Demo( title=self.title, path=path, toc=self.toc, parent=self.parent, descriptions=self.descriptions, snippets=self.snippets, auto_run=True, ) else: self.section = Lecture( title=self.title, path=path, toc=self.toc, parent=self.parent, descriptions=self.descriptions, ) elif len(self.snippets) > 0: self.section = Lab( title=self.title, path=path, toc=self.toc, parent=self.parent, snippets=self.snippets, auto_run=self.auto_run, ) else: # No descriptions or code snippets were found. Create a lecture # anyway: section = Lecture( title=self.title, path=path, toc=self.toc, parent=self.parent, ) # If the lecture has subsections, then return the lecture and add # a default item containing a description of the subsections of the # lecture: if len(section.subsections) > 0: self._create_html_item( path=path, content=DefaultLecture % ('\n'.join( ['
  • %s
  • ' % subsection.title for subsection in section.subsections])) ) section.descriptions = self.descriptions self.section = section def _get_css_path(self): """ Returns the path to the CSS style sheet for the section. """ return css_path_for(self.path, self.parent) def _add_py_item(self, path): """ Creates the code snippets for a Python source file. """ source = read_file(path) if source is not None: lines = source.replace('\r', '').split('\n') start_line = 0 title = 'Prologue' type = IsCode for i, line in enumerate(lines): match = section_pat1.match(line) if match is not None: next_type = IsCode else: match = section_pat2.match(line) if match is not None: next_type = IsHiddenCode else: next_type = IsDescription match = section_pat3.match(line) if match is not None: self._add_snippet(title, path, lines, start_line, i - 1, type) start_line = i + 1 title = match.group(1).strip() type = next_type self._add_snippet(title, path, lines, start_line, i, type) def _add_txt_item(self, path): """ Creates a description item for a normal text file. """ self.descriptions.append(TextItem(path=path)) def _add_htm_item(self, path): """ Creates a description item for an HTML file. """ # Check if there is a corresponding .rst (restructured text) file: dir, base_name = os.path.split(path) rst = os.path.join(dir, os.path.splitext(base_name)[0] + '.rst') # If no .rst file exists, just add the file as a normal HTML file: if not os.path.isfile(rst): self._create_html_item(path=path) def _add_html_item(self, path): """ Creates a description item for an HTML file. """ self._add_htm_item(path) def _add_url_item(self, path): """ Creates a description item for a file containing URLs. """ data = read_file(path) if data is not None: for url in [line for line in data.split('\n') if line.strip()[:1] not in ('', '#')]: self._create_html_item(url=url.strip()) def _add_rst_item(self, path): """ Creates a description item for a ReSTructured text file. """ # If docutils is not installed, just process the file as an ordinary # text file: try: from docutils.core import publish_cmdline except: self._add_txt_item(path) return # Get the name of the HTML file we will write to: dir, base_name = os.path.split(path) html = os.path.join(dir, os.path.splitext(base_name)[0] + '.htm') # Try to find a CSS style sheet, and set up the docutil overrides if # found: settings = {} css_path = self.css_path if css_path != '': css_path = os.path.join(self.path, css_path) settings['stylesheet_path'] = css_path settings['embed_stylesheet'] = True settings['stylesheet'] = None else: css_path = path # If the HTML file does not exist, or is older than the restructured # text file, then let docutils convert it to HTML: is_file = os.path.isfile(html) if ((not is_file) or (os.path.getmtime(path) > os.path.getmtime(html)) or (os.path.getmtime(css_path) > os.path.getmtime(html))): # Delete the current HTML file (if any): if is_file: os.remove(html) # Let docutils create a new HTML file from the restructured text # file: publish_cmdline(writer_name='html', argv=[path, html], settings_overrides=settings) if os.path.isfile(html): # If there is now a valid HTML file, use it: self._create_html_item(path=html) else: # Otherwise, just use the original restructured text file: self._add_txt_item(path) def _add_swf_item(self, path): """ Creates a description item for a Flash file. """ if is_windows: self.descriptions.append(FlashItem(path=path)) def _add_mov_item(self, path): """ Creates a description item for a QuickTime movie file. """ path2 = path.replace(':', '|') self._create_html_item(path=path, content=QTMovieTemplate % (path2, path2)) def _add_wmv_item(self, path): """ Creates a description item for a Windows movie file. """ self._create_html_item(path=path, content=WMVMovieTemplate % (path, path)) def _add_avi_item(self, path): """ Creates a description item for an AVI movie file. """ self._add_wmv_item(path) def _add_jpg_item(self, path): """ Creates a description item for a JPEG image file. """ self._create_html_item(path=path, content=ImageTemplate % path) def _add_jpeg_item(self, path): """ Creates a description item for a JPEG image file. """ self._add_jpg_item(path) def _add_png_item(self, path): """ Creates a description item for a PNG image file. """ self._add_jpg_item(path) def _add_mp3_item(self, path): """ Creates a description item for an mp3 audio file. """ self._create_html_item(path=path, content=MP3Template % path) def _add_desc_item(self, path): """ Creates a section title from a description file. """ # If we've already processed a description file, then we're done: if len(self.toc) > 0: return lines = [] desc = read_file(path) if desc is not None: # Split the file into lines and save the non-empty, non-comment # lines: for line in desc.split('\n'): line = line.strip() if (len(line) > 0) and (line[0] != '#'): lines.append(line) if len(lines) == 0: # If the file didn't have anything useful in it, set a title based # on the description file name: self.title = title_for(os.path.splitext(os.path.basename(path))[0]) else: # Otherwise, set the title and table of contents from the lines in # the file: self.title = lines[0] self.toc = lines[1:] def _add_snippet(self, title, path, lines, start_line, end_line, type): """ Adds a new code snippet or restructured text item to the list of code snippet or description items. """ # Trim leading and trailing blank lines from the snippet: while start_line <= end_line: if lines[start_line].strip() != '': break start_line += 1 while end_line >= start_line: if lines[end_line].strip() != '': break end_line -= 1 # Only add if the snippet is not empty: if start_line <= end_line: # Check for the title containing the 'auto-run' flag ('*'): if title[:1] == '*': self.auto_run = True title = title[1:].strip() if title[-1:] == '*': self.auto_run = True title = title[:-1].strip() # Extract out just the lines we will use: content_lines = lines[start_line: end_line + 1] if type == IsDescription: # Add the new restructured text description: self._add_description(content_lines, title) else: # Add the new code snippet: self.snippets.append(CodeItem( title=title or 'Code', path=path, hidden=(type == IsHiddenCode), content='\n'.join(content_lines) )) def _add_description(self, lines, title): """ Converts a restructured text string to HTML and adds it as description item. """ # Scan the lines for any imbedded Python code that should be shown as # a separate snippet: i = 0 while i < len(lines): if lines[i].strip()[-2:] == '::': i = self._check_embedded_code(lines, i + 1) else: i += 1 # Strip off any docstring style triple quotes (if necessary): content = '\n'.join(lines).strip() if content[:3] in ('"""', "'''"): content = content[3:] if content[-3:] in('"""', "'''"): content = content[:-3] content = content.strip() # If docutils is not installed, just add it as a text string item: try: from docutils.core import publish_string except: self.descriptions.append(TextStrItem(content=content, title=title)) return # Try to find a CSS style sheet, and set up the docutil overrides if # found: settings = {} css_path = self.css_path if css_path != '': css_path = os.path.join(self.path, css_path) settings['stylesheet_path'] = css_path settings['embed_stylesheet'] = True settings['stylesheet'] = None # Convert it from restructured text to HTML: html = publish_string(content, writer_name='html', settings_overrides=settings) # Choose the right HTML renderer: if is_windows: item = IEHTMLStrItem(content=html, title=title) else: item = HTMLStrItem(content=html, title=title) # Add the resulting item to the descriptions list: self.descriptions.append(item) def _create_html_item(self, **traits): """ Creates a platform specific html item and adds it to the list of descriptions. """ if is_windows: item = IEHTMLItem(**traits) else: item = HTMLItem(**traits) self.descriptions.append(item) def _check_embedded_code(self, lines, start): """ Checks for an embedded Python code snippet within a description. """ n = len(lines) while start < n: line = lines[start].strip() if line == '': start += 1 continue if (line[:1] != '[') or (line[-1:] != ']'): break del lines[start] n -= 1 title = line[1:-1].strip() line = lines[start] + '.' pad = len(line) - len(line.strip()) clines = [] while start < n: line = lines[start] + '.' len_line = len(line.strip()) if (len_line > 1) and ((len(line) - len_line) < pad): break if (len(clines) > 0) or (len_line > 1): clines.append(line[pad: -1]) start += 1 # Add the new code snippet: self.snippets.append(CodeItem( title=title or 'Code', content='\n'.join(clines) )) break return start # Tutor tree editor: tree_editor = TreeEditor( nodes=[ TreeNode( children='subsections', label='title', rename=False, copy=False, delete=False, delete_me=False, insert=False, auto_open=True, auto_close=False, node_for=[ASection], icon_group='' ) ], editable=False, auto_open=1, selected='section' ) # 'Tutor' class: class Tutor(HasPrivateTraits): """ The main tutorial class which manages the presentation and navigation of the entire tutorial. """ # The path to the files distributed with the tutor: home = Directory # The path to the root of the tutorial tree: path = Directory # The root of the tutorial lesson tree: root = Instance(ASection) # The current section of the tutorial being displayed: section = Instance(ASection) # The next section: next_section = Property(depends_on='section', cached=True) # The previous section: previous_section = Property(depends_on='section', cached=True) # The previous section button: previous = Button(image=ImageResource('previous'), height_padding=1) # The next section button: next = Button(image=ImageResource('next'), height_padding=1) # The parent section button: parent = Button(image=ImageResource('parent'), height_padding=1) # The reload tutor button: reload = Button(image=ImageResource('reload'), height_padding=1) # The title of the current session: title = Property(depends_on='section') view = View( VGroup( HGroup( Item('previous', style='custom', enabled_when='previous_section is not None', tooltip='Go to previous section', ), Item('parent', style='custom', enabled_when='(section is not None) and ' '(section.parent is not None)', tooltip='Go up one level', ), Item('next', style='custom', enabled_when='next_section is not None', tooltip='Go to next section', ), '_', Item('title', springy=True, editor=TitleEditor(), ), '_', Item('reload', style='custom', tooltip='Reload the tutorial', ), show_labels=False, ), '_', HSplit( Item('root', label='Table of Contents', editor=tree_editor, dock='horizontal', export='DockWindowShell', ), Item('section', id='section', label='Current Lesson', style='custom', resizable=True, dock='horizontal', ), id='splitter', show_labels=False, ) ), title='Python Tutor', id='dmorrill.tutor.tutor:1.0', buttons=NoButtons, resizable=True, width=0.8, height=0.8, ) def _path_changed(self, path): """ Handles the tutorial root path being changed. """ self.init_tutor() def _next_changed(self): """ Displays the next tutorial section. """ self.section = self.next_section def _previous_changed(self): """ Displays the previous tutorial section. """ self.section = self.previous_section def _parent_changed(self): """ Displays the parent of the current tutorial section. """ self.section = self.section.parent def _reload_changed(self): """ Reloads the tutor from the original path specified. """ self.init_tutor() @cached_property def _get_next_section(self): """ Returns the next section of the tutorial. """ next = None section = self.section if len(section.subsections) > 0: next = section.subsections[0] else: parent = section.parent while parent is not None: index = parent.subsections.index(section) if index < (len(parent.subsections) - 1): next = parent.subsections[index + 1] break parent, section = parent.parent, parent return next @cached_property def _get_previous_section(self): """ Returns the previous section of the tutorial. """ previous = None section = self.section parent = section.parent if parent is not None: index = parent.subsections.index(section) if index > 0: previous = parent.subsections[index - 1] while len(previous.subsections) > 0: previous = previous.subsections[-1] else: previous = parent return previous def _get_title(self): """ Returns the title of the current section. """ section = self.section if section is None: return '' return ('%s: %s' % (section.__class__.__name__, section.title)) def init_tutor(self): """ Initials the tutor by creating the root section from the specified path. """ path = self.path title = title_for(os.path.splitext(os.path.basename(path))[0]) section = SectionFactory(title=title).trait_set(path=path).section if section is not None: self.section = self.root = section # Run the program: # Only run the program if we were invoked from the command line: if __name__ == '__main__': # Validate the command line arguments: if len(sys.argv) > 2: print Usage sys.exit(1) # Determine the root path to use for the tutorial files: if len(sys.argv) == 2: path = sys.argv[1] else: path = os.getcwd() # Create a tutor and display the tutorial: tutor = Tutor(home=os.path.dirname(sys.argv[0])).trait_set(path=path) if tutor.root is not None: tutor.configure_traits() else: print """No traits tutorial found in %s. Correct usage is: python tutor.py [tutorial_path] where: tutorial_path = Path to the root of the traits tutorial. If tutorial_path is omitted, the current directory is assumed to be the root of the tutorial.""" % path traits-4.6.0/fixers/000077500000000000000000000000001300633736300143335ustar00rootroot00000000000000traits-4.6.0/fixers/__init__.py000066400000000000000000000000001300633736300164320ustar00rootroot00000000000000traits-4.6.0/fixers/fix_unicode_methods.py000066400000000000000000000010171300633736300207230ustar00rootroot00000000000000from lib2to3 import fixer_base from lib2to3.fixer_util import Name class FixUnicodeMethods(fixer_base.BaseFix): """ Custom fixer for the __unicode__ method Renames __unicode__ methods to __str__. Code used from: http://lucumr.pocoo.org/2010/2/11/porting-to-python-3-a-guide/ """ PATTERN = r"funcdef< 'def' name='__unicode__' parameters< '(' NAME ')' > any+ >" # noqa def transform(self, node, results): name = results['name'] name.replace(Name('__str__', prefix=name.prefix)) traits-4.6.0/image_LICENSE.txt000066400000000000000000000074621300633736300160310ustar00rootroot00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: Project License File ---------------------------------------------------------------------------- Eclipse Eclipse Public License image_LICENSE_Eclipse.txt Enthought BSD 3-Clause LICENSE.txt Nuvola LGPL image_LICENSE_Nuvola.txt Unless stated in this file, icons are the work of Enthought, and are released under a 3 clause BSD license. Files and original authors: ---------------------------------------------------------------------------- docs/source/tutorials/images: application1.png | Enthought application2.png | Enthought application3.png | Enthought code_block1.png | Enthought container.png | Enthought interactive.png | Enthought mpl_figure_editor.png | Enthought traits_thread.png | Enthought traitsui/images: array_node.png | Enthought bool_node.png | Enthought class_node.png | Eclipse complex_node.png | Enthought dialog_warning.png | Nuvola dict_node.png | Enthought float_node.png | Enthought folder-new.png | Nuvola function_node.png | Eclipse int_node.png | Enthought list_node.png | Enthought method_node.png | Eclipse none_node.png | Nuvola object_node.png | Eclipse other_node.png | Enthought set_node.png | Enthought std_horizontal_drag.png | Enthought std_horizontal_splitter.png | Enthought std_tab_active.png | Enthought std_tab_background.png | Enthought std_tab_hover.png | Enthought std_tab_inactive.png | Enthought std_tab.png | Enthought std_vertical_drag.png | Enthought std_vertical_splitter.png | Enthought string_node.png | Enthought traits_node.png | Eclipse tuple_node.png | Enthought examples/demo/: traits_ui_demo.jpg | Enthought examples/demo/Advanced/images: red_flag.png | Nuvola examples/demo/Applications/images: blue_ball.png | Nuvola GG5.png | Enthought header.png | Enthought notebook_close.png | Enthought notebook_open.png | Enthought red_ball.png | Enthought TFB.png | Enthought examples/demo/Extras/images: info.png | Enthought logo_32x32.gif | Enthought logo_48x48.gif | Enthought logo_64x64.gif | Enthought examples/demo/Standard Editors/images: bottom_left_origin.gif | Enthought bottom_right_origin.gif | Enthought top_left_origin.gif | Enthought top_right_origin.gif | Enthought examples/demo/Standard Editors/Popup_versions/images: bottom_left_origin.gif | Enthought bottom_right_origin.gif | Enthought top_left_origin.gif | Enthought top_right_origin.gif | Enthought examples/tutorials/images: next.png | Nuvola parent.png | Nuvola previous.png | Nuvola reload.png | Nuvola run.png | Nuvola traits-4.6.0/image_LICENSE_Eclipse.txt000066400000000000000000000260431300633736300174710ustar00rootroot00000000000000Eclipse Public License - v 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENTS ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i)changes to the Program, and ii)additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributors behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipients responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributors responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipients patent(s), then such Recipients rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipients rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipients rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipients obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. traits-4.6.0/image_LICENSE_Nuvola.txt000066400000000000000000000566451300633736300173640ustar00rootroot00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: * a) The modified work must itself be a software library. * b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. * c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. * d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: * a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) * b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. * c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. * d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. * e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: * a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. * b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. traits-4.6.0/setup.py000066400000000000000000000120661300633736300145520ustar00rootroot00000000000000# Copyright (c) 2008-2013 by Enthought, Inc. # All rights reserved. import os import re import subprocess import sys from setuptools import setup, Extension, find_packages MAJOR = 4 MINOR = 6 MICRO = 0 IS_RELEASED = True VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) # Return the git revision as a string def git_version(): def _minimal_ext_cmd(cmd): # construct minimal environment env = {} for k in ['SYSTEMROOT', 'PATH']: v = os.environ.get(k) if v is not None: env[k] = v # LANGUAGE is used on win32 env['LANGUAGE'] = 'C' env['LANG'] = 'C' env['LC_ALL'] = 'C' out = subprocess.Popen( cmd, stdout=subprocess.PIPE, env=env, ).communicate()[0] return out try: out = _minimal_ext_cmd(['git', 'describe', '--tags']) except OSError: out = '' git_description = out.strip().decode('ascii') expr = r'.*?\-(?P\d+)-g(?P[a-fA-F0-9]+)' match = re.match(expr, git_description) if match is None: git_revision, git_count = 'Unknown', '0' else: git_revision, git_count = match.group('hash'), match.group('count') return git_revision, git_count def write_version_py(filename='traits/_version.py'): template = """\ # THIS FILE IS GENERATED FROM TRAITS SETUP.PY version = '{version}' full_version = '{full_version}' git_revision = '{git_revision}' is_released = {is_released} if not is_released: version = full_version """ # Adding the git rev number needs to be done inside # write_version_py(), otherwise the import of traits._version messes # up the build under Python 3. fullversion = VERSION if os.path.exists('.git'): git_rev, dev_num = git_version() elif os.path.exists('traits/_version.py'): # must be a source distribution, use existing version file try: from traits._version import git_revision as git_rev from traits._version import full_version as full_v except ImportError: raise ImportError("Unable to import git_revision. Try removing " "traits/_version.py and the build directory " "before building.") match = re.match(r'.*?\.dev(?P\d+)', full_v) if match is None: dev_num = '0' else: dev_num = match.group('dev_num') else: git_rev = 'Unknown' dev_num = '0' if not IS_RELEASED: fullversion += '.dev{0}'.format(dev_num) with open(filename, "wt") as fp: fp.write(template.format(version=VERSION, full_version=fullversion, git_revision=git_rev, is_released=IS_RELEASED)) if __name__ == "__main__": write_version_py() from traits import __version__ ctraits = Extension( 'traits.ctraits', sources=['traits/ctraits.c'], extra_compile_args=['-DNDEBUG=1', '-O3'], ) def additional_commands(): # Pygments 2 isn't supported on Python 3 versions earlier than 3.3, so # don't make the documentation command available there. if (3,) <= sys.version_info < (3, 3): return {} try: from sphinx.setup_command import BuildDoc except ImportError: return {} else: return {'documentation': BuildDoc} setup( name='traits', version=__version__, url='http://docs.enthought.com/traits', author='David C. Morrill, et. al.', author_email='info@enthought.com', classifiers=[c.strip() for c in """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers Intended Audience :: Science/Research License :: OSI Approved :: BSD License Operating System :: MacOS Operating System :: Microsoft :: Windows Operating System :: OS Independent Operating System :: POSIX Operating System :: Unix Programming Language :: C Programming Language :: Python Topic :: Scientific/Engineering Topic :: Software Development Topic :: Software Development :: Libraries """.splitlines() if len(c.strip()) > 0], description='explicitly typed attributes for Python', long_description=open('README.rst').read(), download_url='https://github.com/enthought/traits', ext_modules=[ctraits], license='BSD', maintainer='ETS Developers', maintainer_email='enthought-dev@enthought.com', packages=find_packages(exclude=['fixers']), platforms=["Windows", "Linux", "Mac OS-X", "Unix", "Solaris"], zip_safe=False, use_2to3=True, use_2to3_fixers=['fixers'], # traits_listener.ListenerItem has a trait *next* which gets # wrongly renamed use_2to3_exclude_fixers=['lib2to3.fixes.fix_next'], cmdclass=additional_commands(), ) traits-4.6.0/tox.ini000066400000000000000000000002151300633736300143440ustar00rootroot00000000000000[tox] envlist = py{26,27,33,34,35} [testenv] deps = -rtravis-ci-requirements.txt changedir = .tox commands = python -m nose.core -v traits traits-4.6.0/traits/000077500000000000000000000000001300633736300143415ustar00rootroot00000000000000traits-4.6.0/traits/__init__.py000066400000000000000000000007231300633736300164540ustar00rootroot00000000000000from __future__ import absolute_import from traits._version import full_version as __version__ # Add a NullHandler so 'traits' loggers don't complain when they get used. import logging class NullHandler(logging.Handler): def handle(self, record): pass def emit(self, record): pass def createLock(self): self.lock = None logger = logging.getLogger(__name__) logger.addHandler(NullHandler()) del logging, logger, NullHandler traits-4.6.0/traits/_py2to3.py000066400000000000000000000173021300633736300162150ustar00rootroot00000000000000""" Helper module, providing a common API for tasks that require a different implementation in python 2 and 3. """ from __future__ import division, absolute_import import sys if sys.version_info[0] < 3: import string str_find = string.find str_rfind = string.rfind else: str_find = str.find str_rfind = str.rfind if sys.version_info[0] < 3: from types import InstanceType,ClassType def is_old_style_instance(obj): return type(obj) is InstanceType def is_old_style_class(obj): return type(obj) is ClassType def is_InstanceType(obj): return obj is InstanceType def is_ClassType(obj): return obj is ClassType else: def is_old_style_instance(obj): return False def is_old_style_instance(obj): return False def is_InstanceType(obj): return False def is_ClassType(obj): return False if sys.version_info[0] < 3: from types import InstanceType def type_w_old_style(obj): the_type = type(obj) if the_type is InstanceType: # Old-style class. the_type = obj.__class__ return the_type else: type_w_old_style = type if sys.version_info[0] < 3: from types import ClassType ClassTypes = ( ClassType, type ) else: ClassTypes = ( type, ) import contextlib if sys.version_info[0] < 3: def nested_context_mgrs(*args): return contextlib.nested(*args) else: if sys.version_info[:2] < (3,3): # ExitStack was introduced in python 3.3. We copy the 3.3 version here # to support python 3.2 class ExitStack(object): """Context manager for dynamic management of a stack of exit callbacks For example: with ExitStack() as stack: files = [stack.enter_context(open(fname)) for fname in filenames] # All opened files will automatically be closed at the end of # the with statement, even if attempts to open files later # in the list raise an exception """ def __init__(self): from collections import deque self._exit_callbacks = deque() def pop_all(self): """Preserve the context stack by transferring it to a new instance""" from collections import deque new_stack = type(self)() new_stack._exit_callbacks = self._exit_callbacks self._exit_callbacks = deque() return new_stack def _push_cm_exit(self, cm, cm_exit): """Helper to correctly register callbacks to __exit__ methods""" def _exit_wrapper(*exc_details): return cm_exit(cm, *exc_details) _exit_wrapper.__self__ = cm self.push(_exit_wrapper) def push(self, exit): """Registers a callback with the standard __exit__ method signature Can suppress exceptions the same way __exit__ methods can. Also accepts any object with an __exit__ method (registering a call to the method instead of the object itself) """ # We use an unbound method rather than a bound method to follow # the standard lookup behaviour for special methods _cb_type = type(exit) try: exit_method = _cb_type.__exit__ except AttributeError: # Not a context manager, so assume its a callable self._exit_callbacks.append(exit) else: self._push_cm_exit(exit, exit_method) return exit # Allow use as a decorator def callback(self, callback, *args, **kwds): """Registers an arbitrary callback and arguments. Cannot suppress exceptions. """ def _exit_wrapper(exc_type, exc, tb): callback(*args, **kwds) # We changed the signature, so using @wraps is not appropriate, but # setting __wrapped__ may still help with introspection _exit_wrapper.__wrapped__ = callback self.push(_exit_wrapper) return callback # Allow use as a decorator def enter_context(self, cm): """Enters the supplied context manager If successful, also pushes its __exit__ method as a callback and returns the result of the __enter__ method. """ # We look up the special methods on the type to match the with statement _cm_type = type(cm) _exit = _cm_type.__exit__ result = _cm_type.__enter__(cm) self._push_cm_exit(cm, _exit) return result def close(self): """Immediately unwind the context stack""" self.__exit__(None, None, None) def __enter__(self): return self def __exit__(self, *exc_details): # We manipulate the exception state so it behaves as though # we were actually nesting multiple with statements frame_exc = sys.exc_info()[1] def _fix_exception_context(new_exc, old_exc): while 1: exc_context = new_exc.__context__ if exc_context in (None, frame_exc): break new_exc = exc_context new_exc.__context__ = old_exc # Callbacks are invoked in LIFO order to match the behaviour of # nested context managers suppressed_exc = False while self._exit_callbacks: cb = self._exit_callbacks.pop() try: if cb(*exc_details): suppressed_exc = True exc_details = (None, None, None) except: new_exc_details = sys.exc_info() # simulate the stack of exceptions by setting the context _fix_exception_context(new_exc_details[1], exc_details[1]) if not self._exit_callbacks: raise exc_details = new_exc_details return suppressed_exc else: ExitStack = contextlib.ExitStack class nested_context_mgrs(ExitStack): """ Emulation of python 2's :py:class:`contextlib.nested`. It has gone from python 3 due to it's deprecation status in python 2. Note that :py:class:`contextlib.nested` was deprecated for a reason: It has issues with context managers that fail during init. The same caveats also apply here. So do not use this unless really necessary! """ def __init__(self,*args): super(nested_context_mgrs,self).__init__() self._ctxt_mgrs = args def __enter__(self): ret = [] try: for mgr in self._ctxt_mgrs: ret.append(self.enter_context(mgr)) except: self.close() raise return tuple(ret) if sys.version_info[0] < 3: def assertCountEqual(self,itemsA,itemsB): self.assertItemsEqual(itemsA,itemsB) else: def assertCountEqual(self,itemsA,itemsB): self.assertCountEqual(itemsA,itemsB) traits-4.6.0/traits/adaptation/000077500000000000000000000000001300633736300164655ustar00rootroot00000000000000traits-4.6.0/traits/adaptation/__init__.py000066400000000000000000000012151300633736300205750ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. #------------------------------------------------------------------------------ """ Adaptation package. :Copyright: 2013 Enthought, Inc. """ traits-4.6.0/traits/adaptation/adaptation_error.py000066400000000000000000000015321300633736300223750ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. #------------------------------------------------------------------------------ """ Exception raised when a requested adaptation is not possible. """ class AdaptationError(TypeError): """ Exception raised when a requested adaptation is not possible. """ pass #### EOF ###################################################################### traits-4.6.0/traits/adaptation/adaptation_manager.py000066400000000000000000000366251300633736300226710ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. #------------------------------------------------------------------------------ """ Manages all registered adaptations. """ from heapq import heappop, heappush import inspect import itertools import sys import functools from traits.adaptation.adaptation_error import AdaptationError from traits.has_traits import HasTraits from traits.trait_types import Dict, List, Str def no_adapter_necessary(adaptee): """ An adapter factory used to register that a protocol provides another. See 'register_provides' for details. """ return adaptee class AdaptationManager(HasTraits): """ Manages all registered adaptations. """ #### 'AdaptationManager' class protocol ################################### @staticmethod def mro_distance_to_protocol(from_type, to_protocol): """ Return the distance in the MRO from 'from_type' to 'to_protocol'. If `from_type` provides `to_protocol`, returns the distance between `from_type` and the super-most class in the MRO hierarchy providing `to_protocol` (that's where the protocol was provided in the first place). If `from_type` does not provide `to_protocol`, return None. """ if not AdaptationManager.provides_protocol(from_type, to_protocol): return None # We walk up the MRO hierarchy until the point where the `to_protocol` # is *no longer* provided. When we reach that point we know that the # previous class in the MRO is the one that provided the protocol in # the first place (e.g., the first super-class implementing an # interface). supertypes = inspect.getmro(from_type)[1:] distance = 0 for t in supertypes: if AdaptationManager.provides_protocol(t, to_protocol): distance += 1 # We have reached the point in the MRO where the protocol is no # longer provided. else: break return distance @staticmethod def provides_protocol(type_, protocol): """ Does the given type provide (i.e implement) a given protocol? Parameters ---------- type_ : Python 'type'. protocol : Either a regular Python class or a traits Interface. Returns ------- result : bool True if the object provides the protocol, otherwise False. """ # We do the 'is' check first as a performance improvement to save us # a call to 'issubclass'. return type_ is protocol or issubclass(type_, protocol) #### 'AdaptationManager' protocol ########################################## def adapt(self, adaptee, to_protocol, default=AdaptationError): """ Attempt to adapt an object to a given protocol. Parameters ---------- adaptee : The object that we want to adapt. to_protocol : The protocol that the want to adapt the object to. If `adaptee` already provides (i.e. implements) the given protocol then it is simply returned unchanged. Otherwise, we try to build a chain of adapters that adapt `adaptee` to `to_protocol`. If no such adaptation is possible then either an AdaptationError is raised (if default=Adaptation error), or `default` is returned (as in the default value passed to 'getattr' etc). """ # If the object already provides the given protocol then it is # simply returned. # We use adaptee.__class__ instead of type(adaptee) as a courtesy to # old-style classes. if self.provides_protocol(adaptee.__class__, to_protocol): result = adaptee # Otherwise, try adapting the object. else: result = self._adapt(adaptee, to_protocol) if result is None: if default is AdaptationError: raise AdaptationError( 'Could not adapt %r to %r' % (adaptee, to_protocol)) else: result = default return result def register_offer(self, offer): """ Register an offer to adapt from one protocol to another. """ offers = self._adaptation_offers.setdefault( offer.from_protocol_name, [] ) offers.append(offer) return def register_factory(self, factory, from_protocol, to_protocol): """ Register an adapter factory. This is a simply a convenience method that creates and registers an 'AdaptationOffer' from the given arguments. """ from traits.adaptation.adaptation_offer import AdaptationOffer self.register_offer( AdaptationOffer( factory = factory, from_protocol = from_protocol, to_protocol = to_protocol ) ) return def register_provides(self, provider_protocol, protocol): """ Register that a protocol provides another. """ self.register_factory(no_adapter_necessary, provider_protocol, protocol) return def supports_protocol(self, obj, protocol): """ Does the object support a given protocol? An object "supports" a protocol if either it "provides" it directly, or it can be adapted to it. """ return self.adapt(obj, protocol, None) is not None #### Private protocol ##################################################### #: All registered adaptation offers. #: Keys are the type name of the offer's from_protocol; values are a #: list of adaptation offers. _adaptation_offers = Dict(Str, List) def _adapt(self, adaptee, to_protocol): """ Returns an adapter that adapts an object to the target class. Returns None if no such adapter exists. """ # The algorithm for finding a sequence of adapters adapting 'adaptee' # to 'to_protocol' is based on a weighted graph. # Nodes on the graphs are protocols (types or interfaces). # Edges are adaptation offers that connect a offer.from_protocol to a # offer.to_protocol. # Edges connect protocol A to protocol B and are weighted by two # numbers in this priority: # 1) a unit weight (1) representing the fact that we use 1 adaptation # offer to go from A to B # 2) the number of steps up the type hierarchy that we need to take # to go from A to offer.from_protocol, so that more specific # adapters are always preferred # The algorithm finds the shortest weighted path between 'adaptee' # and 'to_protocol'. Once a candidate path is found, it tries to # create the adapters using the factories in the adaptation offers # that compose the path. If this fails because of conditional # adaptation (i.e., an adapter factory returns None), the path # is discarded and the algorithm looks for the next shortest path. # Cycles in adaptation are avoided by only considering path were # every adaptation offer is used at most once. # The implementation of the algorithm is based on a priority queue, # 'offer_queue'. # # Each value in the queue has got two parts, # one is the adaptation path, i.e., the sequence of adaptation offers # followed so far; the second value is the protocol of the last # visited node. # # The priority in the queue is the sum of all the weights for the # edges traversed in the path. # Unique sequence counter to make the priority list stable # w.r.t the sequence of insertion. counter = itertools.count() # The priority queue containing entries of the form # (cumulative weight, path, current protocol) describing an # adaptation path starting at `adaptee`, following a sequence # of adaptation offers, `path`, and having weight `cumulative_weight`. # # 'cumulative weight' is a tuple of the form # (number of traversed adapters, # number of steps up protocol hierarchies, # counter) # # The counter is an increasing number, and is used to make the # priority queue stable w.r.t insertion time # (see http://bit.ly/13VxILn). offer_queue = [((0, 0, next(counter)), [], type(adaptee))] while len(offer_queue) > 0: # Get the most specific candidate path for adaptation. weight, path, current_protocol = heappop(offer_queue) edges = self._get_applicable_offers(current_protocol, path) # Sort by weight first, then by from_protocol type. if sys.version_info[0] < 3: edges.sort(cmp=_by_weight_then_from_protocol_specificity) else: # functools.cmp_to_key is available from 2.7 and 3.2 edges.sort(key=functools.cmp_to_key(_by_weight_then_from_protocol_specificity)) # At this point, the first edges are the shortest ones. Within # edges with the same distance, interfaces which are subclasses # of other interfaces in that group come first. The rest of # the order is unspecified. for mro_distance, offer in edges: new_path = path + [offer] # Check if we arrived at the target protocol. if self.provides_protocol(offer.to_protocol, to_protocol): # Walk path and create adapters adapter = adaptee for offer in new_path: adapter = offer.factory(adapter) if adapter is None: # This adaptation attempt failed (e.g. because of # conditional adaptation). # Discard this path and continue. break else: # We're done! return adapter else: # Push the new path on the priority queue. adapter_weight, mro_weight, _ = weight new_weight = (adapter_weight + 1, mro_weight + mro_distance, next(counter)) heappush( offer_queue, (new_weight, new_path, offer.to_protocol) ) return None def _get_applicable_offers(self, current_protocol, path): """ Find all adaptation offers that can be applied to a protocol. Return all the applicable offers together with the number of steps up the MRO hierarchy that need to be taken from the protocol to the offer's from_protocol. The returned object is a list of tuples (mro_distance, offer) . In terms of our graph algorithm, we're looking for all outgoing edges from the current node. """ edges = [] for from_protocol_name, offers in self._adaptation_offers.items(): from_protocol = offers[0].from_protocol mro_distance = self.mro_distance_to_protocol( current_protocol, from_protocol ) if mro_distance is not None: for offer in offers: # Avoid cycles by checking that we did not consider this # offer in this path. if offer not in path: edges.append((mro_distance, offer)) return edges def _by_weight_then_from_protocol_specificity(edge_1, edge_2): """ Comparison function for graph edges. Each edge is of the form (mro distance, adaptation offer). Comparison is done by mro distance first, and by offer's from_protocol issubclass next. If two edges have the same mro distance, and the from_protocols of the two edges are not subclasses of one another, they are considered "equal". """ # edge_1 and edge_2 are edges, of the form (mro_distance, offer) mro_distance_1, offer_1 = edge_1 mro_distance_2, offer_2 = edge_2 # First, compare the MRO distance. if mro_distance_1 < mro_distance_2: return -1 elif mro_distance_1 > mro_distance_2: return 1 # The distance is equal, prefer more specific 'from_protocol's if offer_1.from_protocol is offer_2.from_protocol: return 0 if issubclass(offer_1.from_protocol, offer_2.from_protocol): return -1 elif issubclass(offer_2.from_protocol, offer_1.from_protocol): return 1 return 0 #: The default global adaptation manager. #: #: PROVIDED FOR BACKWARD COMPATIBILITY ONLY, IT SHOULD NEVER BE USED DIRECTLY. #: If you must use a global adaptation manager, use the functions #: :class:`get_global_adaptation_manager`, #: :class:`reset_global_adaptation_manager`, #: :class:`set_global_adaptation_manager`. adaptation_manager = AdaptationManager() def set_global_adaptation_manager(new_adaptation_manager): """ Set the global adaptation manager to the given instance. """ global adaptation_manager adaptation_manager = new_adaptation_manager def reset_global_adaptation_manager(): """ Set the global adaptation manager to a new AdaptationManager instance. """ global adaptation_manager adaptation_manager = AdaptationManager() def get_global_adaptation_manager(): """ Set a reference to the global adaptation manager. """ global adaptation_manager return adaptation_manager # Convenience references to methods on the default adaptation manager. # # If you add a public method to the adaptation manager protocol then don't # forget to add a convenience function here! def adapt(adaptee, to_protocol, default=AdaptationError): """ Attempt to adapt an object to a given protocol. """ manager = get_global_adaptation_manager() return manager.adapt(adaptee, to_protocol, default) def register_factory(factory, from_protocol, to_protocol): """ Register an adapter factory. """ manager = get_global_adaptation_manager() return manager.register_factory(factory, from_protocol, to_protocol) def register_offer(offer): """ Register an offer to adapt from one protocol to another. """ manager = get_global_adaptation_manager() return manager.register_offer(offer) def register_provides(provider_protocol, protocol): """ Register that a protocol provides another. """ manager = get_global_adaptation_manager() return manager.register_provides(provider_protocol, protocol) def supports_protocol(obj, protocol): """ Does the object support a given protocol? """ manager = get_global_adaptation_manager() return manager.supports_protocol(obj, protocol) def provides_protocol(type_, protocol): """ Does the given type provide (i.e implement) a given protocol? """ return AdaptationManager.provides_protocol(type_, protocol) #### EOF ###################################################################### traits-4.6.0/traits/adaptation/adaptation_offer.py000066400000000000000000000124731300633736300223530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. #------------------------------------------------------------------------------ """ An offer to provide adapters from one protocol to another. """ from traits.api import Any, Bool, HasTraits, Property from traits.util.api import import_symbol class AdaptationOffer(HasTraits): """ An offer to provide adapters from one protocol to another. An adaptation offer consists of a factory that can create adapters, and the protocols that define what the adapters adapt from and to. """ #### 'object' protocol #################################################### def __repr__(self): """ Return a string representation of the object. """ template = " '{to}'>" from_ = self.from_protocol_name to = self.to_protocol_name return template.format(from_=from_, to=to) #### 'AdaptationOffer' protocol ########################################### #: A factory for creating adapters. #: #: The factory must ba callable that takes exactly one argument which is #: the object to be adapted (known as the adaptee), and returns an #: adapter from the `from_protocol` to the `to_protocol`. #: #: The factory can be specified as either a callable, or a string in the #: form 'foo.bar.baz' which is turned into an import statement #: 'from foo.bar import baz' and imported when the trait is first accessed. factory = Property(Any) #: Adapters created by the factory adapt *from* this protocol. #: #: The protocol can be specified as a protocol (class/Interface), or a #: string in the form 'foo.bar.baz' which is turned into an import #: statement 'from foo.bar import baz' and imported when the trait is #: accessed. from_protocol = Property(Any) from_protocol_name = Property(Any) def _get_from_protocol_name(self): return self._get_type_name(self._from_protocol) #: Adapters created by the factory adapt *to* this protocol. #: #: The protocol can be specified as a protocol (class/Interface), or a #: string in the form 'foo.bar.baz' which is turned into an import #: statement 'from foo.bar import baz' and imported when the trait is #: accessed. to_protocol = Property(Any) to_protocol_name = Property(Any) def _get_to_protocol_name(self): return self._get_type_name(self._to_protocol) #### Private protocol ###################################################### #: Shadow trait for the corresponding property. _factory = Any _factory_loaded = Bool(False) def _get_factory(self): """ Trait property getter. """ if not self._factory_loaded: if isinstance(self._factory, basestring): self._factory = import_symbol(self._factory) self._factory_loaded = True return self._factory def _set_factory(self, factory): """ Trait property setter. """ self._factory = factory return #: Shadow trait for the corresponding property. _from_protocol = Any _from_protocol_loaded = Bool(False) def _get_from_protocol(self): """ Trait property getter. """ if not self._from_protocol_loaded: if isinstance(self._from_protocol, basestring): self._from_protocol = import_symbol(self._from_protocol) self._from_protocol_loaded = True return self._from_protocol def _set_from_protocol(self, from_protocol): """ Trait property setter. """ self._from_protocol = from_protocol return #: Shadow trait for the corresponding property. _to_protocol = Any _to_protocol_loaded = Bool(False) def _get_to_protocol(self): """ Trait property getter. """ if not self._to_protocol_loaded: if isinstance(self._to_protocol, basestring): self._to_protocol = import_symbol(self._to_protocol) self._to_protocol_loaded = True return self._to_protocol def _set_to_protocol(self, to_protocol): """ Trait property setter. """ self._to_protocol = to_protocol return def _get_type_name(self, type_or_type_name): """ Returns the full dotted path for a type. For example: from traits.api import HasTraits _get_type_name(HasTraits) == 'traits.has_traits.HasTraits' If the type is given as a string (e.g., for lazy loading), it is just returned. """ if isinstance(type_or_type_name, basestring): type_name = type_or_type_name else: type_name = "{module}.{name}".format( module = type_or_type_name.__module__, name = type_or_type_name.__name__ ) return type_name #### EOF ###################################################################### traits-4.6.0/traits/adaptation/adapter.py000066400000000000000000000121471300633736300204640ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. #------------------------------------------------------------------------------ """ Base classes for adapters. Adapters do not have to inherit from these classes, as long as their constructor takes the object to be adapted as the first and only *positional* argument. """ from traits.has_traits import HasTraits from traits.trait_types import Any from traits.util.deprecated import deprecated class PurePythonAdapter(object): """ Base class for pure Python adapters. """ def __init__(self, adaptee): """ Constructor. """ self.adaptee = adaptee return class Adapter(HasTraits): """ Base class for adapters with traits. """ def __init__(self, adaptee, **traits): """ Constructor. """ traits['adaptee'] = adaptee super(Adapter, self).__init__(**traits) return adaptee = Any def adapts(from_, to, extra=None, factory=None, cached=False, when=''): """ A class advisor for declaring adapters. Parameters ---------- from_ : type or interface What the adapter adapts *from*, or a list of such types or interfaces (the '_' suffix is used because 'from' is a Python keyword). to : type or interface What the adapter adapts *to*, or a list of such types or interfaces. factory : callable An (optional) factory for actually creating the adapters. This is any callable that takes a single argument which is the object to be adapted. The factory should return an adapter if it can perform the adaptation and **None** if it cannot. cached : bool Should the adapters be cached? If an adapter is cached, then the factory will produce at most one adapter per instance. when : str A Python expression that selects which instances of a particular type can be adapted by this factory. The expression is evaluated in a namespace that contains a single name *adaptee*, which is bound to the object to be adapted (e.g., 'adaptee.is_folder'). Note ---- The ``cached`` and ``when`` arguments are ignored if ``factory`` is specified. """ from traits.adaptation.api import register_factory from traits.adaptation.cached_adapter_factory import CachedAdapterFactory from traits.protocols.advice import addClassAdvisor if extra is not None: adapter, from_, to = from_, to, extra else: adapter = None @deprecated("use the 'register_factory' function from 'traits.api' instead") def callback(klass): """ Called when the class has been created. """ # At this point:- # # klass is the callable (usually a class) that takes one argument (the # adaptee) and returns an appropriate adapter (or None if the adaptation # is not possible). # What the adapters created by the factory will adapt from. if type(from_) is not list: from_protocols = [from_] else: from_protocols = from_ # What the adapters created by the factory will adapt to. if type(to) is not list: to_protocols = [to] else: to_protocols = to if factory is None: # If the adapter is cached or has a 'when' expression then create a # default factory: adapter_factory = klass if when != '': def _conditional_factory(adaptee, *args, **kw): namespace = {'adaptee': adaptee} if eval(when, namespace, namespace): return klass(adaptee, *args, **kw) return None adapter_factory = _conditional_factory if cached: adapter_factory = CachedAdapterFactory(factory=adapter_factory) else: adapter_factory = factory for from_protocol in from_protocols: for to_protocol in to_protocols: register_factory(adapter_factory, from_protocol, to_protocol) for to_protocol in to_protocols: # We cannot register adapter factories that are functions. (This is # ony relevant when using 'adapts' as a function. if isinstance(klass, type): # We use type(to_protocol) in case the to_protocols implements # its own 'register' method which overrides the ABC method. type(to_protocol).register(to_protocol, klass) return klass if adapter is not None: callback(adapter) else: addClassAdvisor(callback) #### EOF ###################################################################### traits-4.6.0/traits/adaptation/api.py000066400000000000000000000006341300633736300176130ustar00rootroot00000000000000from .adapter import Adapter, adapts, PurePythonAdapter from .adaptation_error import AdaptationError from .adaptation_manager import adapt, AdaptationManager, \ get_global_adaptation_manager, provides_protocol, register_factory, \ register_offer, register_provides, reset_global_adaptation_manager, \ set_global_adaptation_manager, supports_protocol from .adaptation_offer import AdaptationOffer traits-4.6.0/traits/adaptation/cached_adapter_factory.py000066400000000000000000000063231300633736300235010ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. #------------------------------------------------------------------------------ """ An adapter factory that caches adapters per instance. """ import weakref from traits.api import Any, Bool, HasTraits, Property from traits.util.api import import_symbol class CachedAdapterFactory(HasTraits): """ An adapter factory that caches adapters per instance. We provide this class to provide the caching functionality of the old traits 'adapts' implementation. However, note that the cache will not be cleared unless you take care of cleaning the 'adaptee' trait once your adapter are deleted. This class will be removed when the 'adapts' function is removed. """ #### 'object' protocol ##################################################### def __call__(self, adaptee): """ The adapter manager uses callables for adapter factories. """ adapter = self._adapter_cache.get(adaptee, None) if adapter is None: adapter = self.factory(adaptee) self._adapter_cache[adaptee] = adapter return adapter #### 'CachedAdapterFactory' protocol ####################################### #: A callable that actually creates the adapters! #: #: The factory must ba callable that takes exactly one argument which is #: the object to be adapted (known as the adaptee), and returns an #: adapter from the `from_protocol` to the `to_protocol`. #: #: The factory can be specified as either a callable, or a string in the #: form 'foo.bar.baz' which is turned into an import statement #: 'from foo.bar import baz' and imported when the trait is first accessed. factory = Property(Any) #: True if the cache is empty, otherwise False. #: #: This method is mostly here to help testing - the framework does not #: rely on it for any other purpose. is_empty = Property(Bool) def _get_is_empty(self): return len(self._adapter_cache) == 0 #### Private protocol ###################################################### _adapter_cache = Any def __adapter_cache_default(self): return weakref.WeakKeyDictionary() #: Shadow trait for the corresponding property. _factory = Any _factory_loaded = Bool(False) def _get_factory(self): """ Trait property getter. """ if not self._factory_loaded: if isinstance(self._factory, basestring): self._factory = import_symbol(self._factory) self._factory_loaded = True return self._factory def _set_factory(self, factory): """ Trait property setter. """ self._factory = factory return #### EOF ####################################################################### traits-4.6.0/traits/adaptation/tests/000077500000000000000000000000001300633736300176275ustar00rootroot00000000000000traits-4.6.0/traits/adaptation/tests/__init__.py000066400000000000000000000000001300633736300217260ustar00rootroot00000000000000traits-4.6.0/traits/adaptation/tests/abc_examples.py000066400000000000000000000076671300633736300226440ustar00rootroot00000000000000""" Test data for testing the protocol manager with ABCs. """ from abc import ABCMeta from traits.adaptation.api import PurePythonAdapter as Adapter #### 'Power plugs' metaphor ################################################### #### Protocols ################################################################ class UKStandard(object): __metaclass__ = ABCMeta class EUStandard(object): __metaclass__ = ABCMeta class JapanStandard(object): __metaclass__ = ABCMeta class IraqStandard(object): __metaclass__ = ABCMeta #### Implementations ########################################################## class UKPlug(object): pass UKStandard.register(UKPlug) class EUPlug(object): pass EUStandard.register(EUPlug) class JapanPlug(object): pass JapanStandard.register(JapanPlug) class IraqPlug(object): pass IraqStandard.register(IraqPlug) class TravelPlug(object): def __init__(self, mode): self.mode = mode #### Adapters ################################################################# # UK->EU class UKStandardToEUStandard(Adapter): pass EUStandard.register(UKStandardToEUStandard) # EU->Japan class EUStandardToJapanStandard(Adapter): pass JapanStandard.register(EUStandardToJapanStandard) # Japan->Iraq class JapanStandardToIraqStandard(Adapter): pass IraqStandard.register(JapanStandardToIraqStandard) # EU->Iraq class EUStandardToIraqStandard(Adapter): pass IraqStandard.register(EUStandardToIraqStandard) # UK->Japan class UKStandardToJapanStandard(Adapter): pass JapanStandard.register(UKStandardToJapanStandard) # Travel->Japan class TravelPlugToJapanStandard(Adapter): pass JapanStandard.register(TravelPlugToJapanStandard) # Travel->EU class TravelPlugToEUStandard(Adapter): pass EUStandard.register(TravelPlugToEUStandard) #### 'Editor, Scriptable, Undoable' metaphor ################################## class FileType(object): pass class IEditor(object): __metaclass__ = ABCMeta class IScriptable(object): __metaclass__ = ABCMeta class IUndoable(object): __metaclass__ = ABCMeta class FileTypeToIEditor(Adapter): pass IEditor.register(FileTypeToIEditor) IScriptable.register(FileTypeToIEditor) class IScriptableToIUndoable(Adapter): pass IUndoable.register(IScriptableToIUndoable) #### Hierarchy example ######################################################## class IPrintable(object): __metaclass__ = ABCMeta class Editor(object): pass class TextEditor(Editor): pass class EditorToIPrintable(Adapter): pass IPrintable.register(EditorToIPrintable) class TextEditorToIPrintable(Adapter): pass IPrintable.register(TextEditorToIPrintable) #### Interface hierarchy example ############################################## class IPrimate(object): __metaclass__ = ABCMeta class IHuman(IPrimate): pass class IChild(IHuman): pass class IIntermediate(object): __metaclass__ = ABCMeta class ITarget(object): __metaclass__ = ABCMeta class Source(object): pass IChild.register(Source) class IChildToIIntermediate(Adapter): pass IIntermediate.register(IChildToIIntermediate) class IHumanToIIntermediate(Adapter): pass IIntermediate.register(IHumanToIIntermediate) class IPrimateToIIntermediate(Adapter): pass IIntermediate.register(IPrimateToIIntermediate) class IIntermediateToITarget(Adapter): pass ITarget.register(IIntermediateToITarget) #### Non-trivial chaining example ############################################# class IStart(object): __metaclass__ = ABCMeta class IGeneric(object): __metaclass__ = ABCMeta class ISpecific(IGeneric): pass class IEnd(object): __metaclass__ = ABCMeta class Start(object): pass IStart.register(Start) class IStartToISpecific(Adapter): pass ISpecific.register(IStartToISpecific) class IGenericToIEnd(Adapter): pass IEnd.register(IGenericToIEnd) #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/benchmark.py000066400000000000000000000074351300633736300221440ustar00rootroot00000000000000""" Simple benchmarking of the adaptation manager. This is not 'enforced' by any tests (i.e. we currently aren't bound to satisfy any performance criteria - but in the future we might be ;^). """ import abc from pprint import pprint import time from traits.adaptation.adaptation_manager import AdaptationManager from traits.api import Adapter, HasTraits, Interface, provides N_SOURCES = 3 N_ITERATIONS = 100 N_PROTOCOLS = 50 # Create some classes to adapt. create_classes_to_adapt = """ class IFoo{i}(Interface): pass @provides(IFoo{i}) class Foo{i}(HasTraits): pass """ for i in range(N_SOURCES): exec create_classes_to_adapt.format(i=i) # The object that we will try to adapt! foo = Foo1() # Create a lot of other interfaces that we will adapt to. for i in range(N_PROTOCOLS): exec 'class I{i}(Interface): pass'.format(i=i) create_traits_adapter_class = """ @provides(I{target}) class IFoo{source}ToI{target}(Adapter): pass """ # Create adapters from each 'IFooX' to all of the interfaces. for source in range(N_SOURCES): for target in range(N_PROTOCOLS): exec create_traits_adapter_class.format(source=source, target=target) #### traits.adaptation with Interfaces ######################################## adaptation_manager = AdaptationManager() register_ifoox_to_ix = """ adaptation_manager.register_factory( factory = IFoo{source}ToI{target}, from_protocol = IFoo{source}, to_protocol = I{target} ) """ # We register the adapters in reversed order, so that looking for the one # with index 0 will need traversing the whole list. # I.e., we're considering the worst case scenario. for source in range(N_SOURCES): for target in reversed(range(N_PROTOCOLS)): exec register_ifoox_to_ix.format(source=source, target=target) start_time = time.time() for _ in range(N_ITERATIONS): adaptation_manager.adapt(foo, I0) time_per_iter = (time.time() - start_time) / float(N_ITERATIONS) * 1000.0 print 'apptools using Interfaces: %.3f msec per iteration' % time_per_iter #### traits.adaptation with ABCs ############################################## # Create some classes to adapt (using ABCs!). for i in range(N_SOURCES): exec 'class FooABC{i}(object): __metaclass__ = abc.ABCMeta'.format(i=i) exec 'class Foo{i}(object): pass'.format(i=i) exec 'FooABC{i}.register(Foo{i})'.format(i=i) # The object that we will try to adapt! foo = Foo0() # Create a lot of other ABCs! for i in range(N_PROTOCOLS): exec 'class ABC{i}(object): __metaclass__ = abc.ABCMeta'.format(i=i) # Create adapters from 'FooABC' to all of the ABCs. create_abc_adapter_class = """ class FooABC{source}ToABC{target}(object): def __init__(self, adaptee): pass ABC{target}.register(FooABC{source}ToABC{target}) """ for source in range(N_SOURCES): for target in range(N_PROTOCOLS): exec create_abc_adapter_class.format(source=source, target=target) # Register all of the adapters. adaptation_manager = AdaptationManager() register_fooxabc_to_abcx = """ adaptation_manager.register_factory( factory = FooABC{source}ToABC{target}, from_protocol = FooABC{source}, to_protocol = ABC{target} ) """ # We register the adapters in reversed order, so that looking for the one # with index 0 will need traversing the whole list. # I.e., we're considering the worst case scenario. for source in range(N_SOURCES): for target in reversed(range(N_PROTOCOLS)): exec register_fooxabc_to_abcx.format(source=source, target=target) start_time = time.time() for _ in range(N_ITERATIONS): adaptation_manager.adapt(foo, ABC0) time_per_iter = (time.time() - start_time) / float(N_ITERATIONS) * 1000.0 print 'apptools using ABCs: %.3f msec per iteration' % time_per_iter #### EOF ####################################################################### traits-4.6.0/traits/adaptation/tests/interface_examples.py000066400000000000000000000062611300633736300240440ustar00rootroot00000000000000""" Test data for testing the protocol manager with interfaces. """ from traits.api import Adapter, Enum, HasTraits, Interface, provides #### 'Power plugs' metaphor ################################################### #### Protocols ################################################################ class UKStandard(Interface): pass class EUStandard(Interface): pass class JapanStandard(Interface): pass class IraqStandard(Interface): pass #### Implementations ########################################################## @provides(UKStandard) class UKPlug(HasTraits): pass @provides(EUStandard) class EUPlug(HasTraits): pass @provides(JapanStandard) class JapanPlug(HasTraits): pass @provides(IraqStandard) class IraqPlug(HasTraits): pass class TravelPlug(HasTraits): mode = Enum(['Europe', 'Asia']) #### Adapters ################################################################# @provides(EUStandard) class UKStandardToEUStandard(Adapter): pass @provides(JapanStandard) class EUStandardToJapanStandard(Adapter): pass @provides(IraqStandard) class JapanStandardToIraqStandard(Adapter): pass @provides(IraqStandard) class EUStandardToIraqStandard(Adapter): pass @provides(JapanStandard) class UKStandardToJapanStandard(Adapter): pass @provides(JapanStandard) class TravelPlugToJapanStandard(Adapter): pass @provides(EUStandard) class TravelPlugToEUStandard(Adapter): pass #### 'Editor, Scriptable, Undoable' metaphor ################################## class FileType(HasTraits): pass class IEditor(Interface): pass class IScriptable(Interface): pass class IUndoable(Interface): pass @provides(IEditor, IScriptable) class FileTypeToIEditor(Adapter): pass @provides(IUndoable) class IScriptableToIUndoable(Adapter): pass #### Hierarchy example ######################################################## class IPrintable(Interface): pass class Editor(HasTraits): pass class TextEditor(Editor): pass @provides(IPrintable) class EditorToIPrintable(Adapter): pass @provides(IPrintable) class TextEditorToIPrintable(Adapter): pass #### Interface hierarchy example ############################################## class IPrimate(Interface): pass class IHuman(IPrimate): pass class IChild(IHuman): pass class IIntermediate(Interface): pass class ITarget(Interface): pass @provides(IChild) class Source(HasTraits): pass @provides(IIntermediate) class IChildToIIntermediate(Adapter): pass @provides(IIntermediate) class IHumanToIIntermediate(Adapter): pass @provides(IIntermediate) class IPrimateToIIntermediate(Adapter): pass @provides(ITarget) class IIntermediateToITarget(Adapter): pass #### Non-trivial chaining example ############################################# class IStart(Interface): pass class IGeneric(Interface): pass class ISpecific(IGeneric): pass class IEnd(Interface): pass @provides(IStart) class Start(HasTraits): pass @provides(ISpecific) class IStartToISpecific(Adapter): pass @provides(IEnd) class IGenericToIEnd(Adapter): pass #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/lazy_examples.py000066400000000000000000000004451300633736300230610ustar00rootroot00000000000000""" Examples for lazy adapter factories. This module should be only imported when the adaptation takes place. """ class IBar(object): pass class IBarToIFoo(object): pass class IFoo(object): pass #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/test_adaptation_manager.py000066400000000000000000000432141300633736300250620ustar00rootroot00000000000000""" Test the adaptation manager. """ import sys from traits.adaptation.api import AdaptationManager, adapt import traits.adaptation.tests.abc_examples import traits.adaptation.tests.interface_examples from traits.testing.unittest_tools import unittest class TestAdaptationManagerWithABC(unittest.TestCase): """ Test the adaptation manager. """ #: Class attribute pointing at the module containing the example data examples = traits.adaptation.tests.abc_examples #### 'TestCase' protocol ################################################## def setUp(self): """ Prepares the test fixture before each test method is called. """ self.adaptation_manager = AdaptationManager() return def tearDown(self): """ Called immediately after each test method has been called. """ return #### Tests ################################################################ def test_no_adapter_required(self): ex = self.examples plug = ex.UKPlug() # Try to adapt it to its own concrete type. uk_plug = self.adaptation_manager.adapt(plug, ex.UKPlug) # The adaptation manager should simply return the same object. self.assertIs(uk_plug, plug) # Try to adapt it to an ABC that is registered for its type. uk_plug = self.adaptation_manager.adapt(plug, ex.UKStandard) # The adaptation manager should simply return the same object. self.assertIs(uk_plug, plug) return def test_no_adapter_available(self): ex = self.examples plug = ex.UKPlug() # Try to adapt it to a concrete type. eu_plug = self.adaptation_manager.adapt(plug, ex.EUPlug, None) # There should be no way to adapt a UKPlug to a EUPlug. self.assertEqual(eu_plug, None) # Try to adapt it to an ABC. eu_plug = self.adaptation_manager.adapt(plug, ex.EUStandard, None) # There should be no way to adapt a UKPlug to a EUPlug. self.assertEqual(eu_plug, None) return def test_one_step_adaptation(self): ex = self.examples # UKStandard->EUStandard. self.adaptation_manager.register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) plug = ex.UKPlug() # Adapt it to an ABC. eu_plug = self.adaptation_manager.adapt(plug, ex.EUStandard) self.assertIsNotNone(eu_plug) self.assertIsInstance(eu_plug, ex.UKStandardToEUStandard) # We shouldn't be able to adapt it to a *concrete* 'EUPlug' though. eu_plug = self.adaptation_manager.adapt(plug, ex.EUPlug, None) self.assertIsNone(eu_plug) return def test_adapter_chaining(self): ex = self.examples # UKStandard->EUStandard. self.adaptation_manager.register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) # EUStandard->JapanStandard. self.adaptation_manager.register_factory( factory = ex.EUStandardToJapanStandard, from_protocol = ex.EUStandard, to_protocol = ex.JapanStandard ) # Create a UKPlug. uk_plug = ex.UKPlug() # Adapt it to a JapanStandard via the chain. japan_plug = self.adaptation_manager.adapt(uk_plug, ex.JapanStandard) self.assertIsNotNone(japan_plug) self.assertIsInstance(japan_plug, ex.EUStandardToJapanStandard) self.assertIs(japan_plug.adaptee.adaptee, uk_plug) return def test_multiple_paths_unambiguous(self): ex = self.examples # UKStandard->EUStandard. self.adaptation_manager.register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) # EUStandard->JapanStandard. self.adaptation_manager.register_factory( factory = ex.EUStandardToJapanStandard, from_protocol = ex.EUStandard, to_protocol = ex.JapanStandard ) # JapanStandard->IraqStandard. self.adaptation_manager.register_factory( factory = ex.JapanStandardToIraqStandard, from_protocol = ex.JapanStandard, to_protocol = ex.IraqStandard ) # EUStandard->IraqStandard. self.adaptation_manager.register_factory( factory = ex.EUStandardToIraqStandard, from_protocol = ex.EUStandard, to_protocol = ex.IraqStandard ) # Create a UKPlug. uk_plug = ex.UKPlug() # Adapt it to a IraqStandard via the chain. iraq_plug = self.adaptation_manager.adapt(uk_plug, ex.IraqStandard) self.assertIsNotNone(iraq_plug) self.assertIsInstance(iraq_plug, ex.EUStandardToIraqStandard) self.assertIs(iraq_plug.adaptee.adaptee, uk_plug) return def test_multiple_paths_ambiguous(self): ex = self.examples # UKStandard->EUStandard. self.adaptation_manager.register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) # UKStandard->JapanStandard. self.adaptation_manager.register_factory( factory = ex.UKStandardToJapanStandard, from_protocol = ex.UKStandard, to_protocol = ex.JapanStandard ) # JapanStandard->IraqStandard. self.adaptation_manager.register_factory( factory = ex.JapanStandardToIraqStandard, from_protocol = ex.JapanStandard, to_protocol = ex.IraqStandard ) # EUStandard->IraqStandard. self.adaptation_manager.register_factory( factory = ex.EUStandardToIraqStandard, from_protocol = ex.EUStandard, to_protocol = ex.IraqStandard ) # Create a UKPlug. uk_plug = ex.UKPlug() # Adapt it to a IraqStandard via the chain. iraq_plug = self.adaptation_manager.adapt(uk_plug, ex.IraqStandard) self.assertIsNotNone(iraq_plug) self.assertIn( type(iraq_plug), [ex.EUStandardToIraqStandard, ex.JapanStandardToIraqStandard] ) self.assertIs(iraq_plug.adaptee.adaptee, uk_plug) return def test_conditional_adaptation(self): ex = self.examples # TravelPlug->EUStandard. def travel_plug_to_eu_standard(adaptee): if adaptee.mode == 'Europe': return ex.TravelPlugToEUStandard(adaptee=adaptee) else: return None self.adaptation_manager.register_factory( factory = travel_plug_to_eu_standard, from_protocol = ex.TravelPlug, to_protocol = ex.EUStandard ) # Create a TravelPlug. travel_plug = ex.TravelPlug(mode='Europe') # Adapt it to a EUStandard. eu_plug = self.adaptation_manager.adapt(travel_plug, ex.EUStandard) self.assertIsNotNone(eu_plug) self.assertIsInstance(eu_plug, ex.TravelPlugToEUStandard) # Create a TravelPlug. travel_plug = ex.TravelPlug(mode='Asia') # Adapt it to a EUStandard. eu_plug = self.adaptation_manager.adapt(travel_plug, ex.EUStandard, None) self.assertIsNone(eu_plug) return def test_spillover_adaptation_behavior(self): ex = self.examples # FileType->IEditor. self.adaptation_manager.register_factory( factory = ex.FileTypeToIEditor, from_protocol = ex.FileType, to_protocol = ex.IEditor ) # Meanwhile, in a plugin far, far away ... # IScriptable->IPrintable. self.adaptation_manager.register_factory( factory = ex.IScriptableToIUndoable, from_protocol = ex.IScriptable, to_protocol = ex.IUndoable ) # Create a file type. file_type = ex.FileType() # Try to adapt to IPrintable: since we did not define an adapter # chain that goes from FileType to IPrintable, this should fail. printable = self.adaptation_manager.adapt(file_type, ex.IUndoable, None) self.assertIsNone(printable) return def test_adaptation_prefers_subclasses(self): ex = self.examples # TextEditor->IPrintable. self.adaptation_manager.register_factory( factory = ex.TextEditorToIPrintable, from_protocol = ex.TextEditor, to_protocol = ex.IPrintable ) # Editor->IPrintable. self.adaptation_manager.register_factory( factory = ex.EditorToIPrintable, from_protocol = ex.Editor, to_protocol = ex.IPrintable ) # Create a text editor. text_editor = ex.TextEditor() # Adapt to IPrintable: we should get the TextEditorToIPrintable # adapter, not the EditorToIPrintable one. printable = self.adaptation_manager.adapt(text_editor, ex.IPrintable) self.assertIsNotNone(printable) self.assertIs(type(printable), ex.TextEditorToIPrintable) return def test_adaptation_prefers_subclasses_other_registration_order(self): # This test is identical to `test_adaptation_prefers_subclasses` # with adapters registered in the opposite order. Both of them # should pass ex = self.examples # Editor->IPrintable. self.adaptation_manager.register_factory( factory = ex.EditorToIPrintable, from_protocol = ex.Editor, to_protocol = ex.IPrintable ) # TextEditor->IPrintable. self.adaptation_manager.register_factory( factory = ex.TextEditorToIPrintable, from_protocol = ex.TextEditor, to_protocol = ex.IPrintable ) # Create a text editor. text_editor = ex.TextEditor() # Adapt to IPrintable: we should get the TextEditorToIPrintable # adapter, not the EditorToIPrintable one. printable = self.adaptation_manager.adapt(text_editor, ex.IPrintable) self.assertIsNotNone(printable) self.assertIs(type(printable), ex.TextEditorToIPrintable) return def test_circular_adaptation(self): # Circles in the adaptation graph should not lead to infinite loops # when it is impossible to reach the target. class Foo(object): pass class Bar(object): pass # object->Foo self.adaptation_manager.register_factory( factory = lambda adaptee: Foo(), from_protocol = object, to_protocol = Foo ) # Foo->object self.adaptation_manager.register_factory( factory = lambda adaptee: [], from_protocol = Foo, to_protocol = object ) # Create an object. obj = [] # Try to adapt to an unreachable target. bar = self.adaptation_manager.adapt(obj, Bar, None) self.assertIsNone(bar) return def test_default_argument_in_adapt(self): from traits.adaptation.adaptation_manager import AdaptationError # Without a default argument, a failed adaptation raises an error. with self.assertRaises(AdaptationError): self.adaptation_manager.adapt('string', int) # With a default argument, a failed adaptation returns the default. default = 'default' result = self.adaptation_manager.adapt('string', int, default=default) self.assertIs(result, default) return def test_prefer_specific_interfaces(self): ex = self.examples # IIntermediate -> ITarget. self.adaptation_manager.register_factory( factory = ex.IIntermediateToITarget, from_protocol = ex.IIntermediate, to_protocol = ex.ITarget ) # IHuman -> IIntermediate. self.adaptation_manager.register_factory( factory = ex.IHumanToIIntermediate, from_protocol = ex.IHuman, to_protocol = ex.IIntermediate ) # IChild -> IIntermediate. self.adaptation_manager.register_factory( factory = ex.IChildToIIntermediate, from_protocol = ex.IChild, to_protocol = ex.IIntermediate ) # IPrimate -> IIntermediate. self.adaptation_manager.register_factory( factory = ex.IPrimateToIIntermediate, from_protocol = ex.IPrimate, to_protocol = ex.IIntermediate ) # Create a source. source = ex.Source() # Adapt to ITarget: we should get the adapter for the most specific # interface, i.e. IChildToITarget. target = self.adaptation_manager.adapt(source, ex.ITarget) self.assertIsNotNone(target) self.assertIs(type(target.adaptee), ex.IChildToIIntermediate) return def test_chaining_with_intermediate_mro_climbing(self): ex = self.examples # IStart -> ISpecific. self.adaptation_manager.register_factory( factory = ex.IStartToISpecific, from_protocol = ex.IStart, to_protocol = ex.ISpecific ) # IGeneric -> IEnd. self.adaptation_manager.register_factory( factory = ex.IGenericToIEnd, from_protocol = ex.IGeneric, to_protocol = ex.IEnd ) # Create a start. start = ex.Start() # Adapt to IEnd; this should succeed going from IStart to ISpecific, # climbing up the MRO to IGeneric, then crossing to IEnd. end = self.adaptation_manager.adapt(start, ex.IEnd) self.assertIsNotNone(end) self.assertIs(type(end), ex.IGenericToIEnd) return def test_conditional_recycling(self): # Test that an offer that has been considered but failed if considered # again at a later time, when it might succeed because of conditional # adaptation. # C -- A -fails- B # C -- D -- A -succeeds- B class A(object): def __init__(self, allow_adaptation): self.allow_adaptation = allow_adaptation class B(object): pass class C(object): pass class D(object): pass self.adaptation_manager.register_factory( factory=lambda adaptee: A(False), from_protocol=C, to_protocol=A ) self.adaptation_manager.register_factory( factory=lambda adaptee: A(True), from_protocol=D, to_protocol=A ) self.adaptation_manager.register_factory( factory=lambda adaptee: D(), from_protocol=C, to_protocol=D ) # Conditional adapter def a_to_b_adapter(adaptee): if adaptee.allow_adaptation: b = B() b.marker = True else: b = None return b self.adaptation_manager.register_factory( factory=a_to_b_adapter, from_protocol=A, to_protocol=B ) # Create a A c = C() # Adaptation to B should succeed through D b = self.adaptation_manager.adapt(c, B) self.assertIsNotNone(b) self.assertTrue(hasattr(b, 'marker')) return def test_provides_protocol_for_interface_subclass(self): from traits.api import Interface class IA(Interface): pass class IB(IA): pass self.assertTrue(self.adaptation_manager.provides_protocol(IB, IA)) return def test_register_provides(self): from traits.api import Interface class IFoo(Interface): pass obj = {} self.assertEqual(None, self.adaptation_manager.adapt(obj, IFoo, None)) self.adaptation_manager.register_provides(dict, IFoo) self.assertEqual(obj, self.adaptation_manager.adapt(obj, IFoo)) return class TestAdaptationManagerWithInterfaces(TestAdaptationManagerWithABC): """ Test the adaptation manager with Interfaces. """ examples = traits.adaptation.tests.interface_examples def test_adapts_should_register_class_as_providing_the_to_protocol(self): if sys.version_info[0] >= 3: self.skipTest(""" Currently, under Python 3, class advisors do not work anymore. Therefore, this test will fail due to the use of "adapts". """) from traits.api import Adapter, adapts, HasTraits, Instance, \ Int, Interface class IFoo(Interface): x = Int class Bar(HasTraits): foo = Instance(IFoo) class Baz(HasTraits): pass # Warning: because we are trying to test the 'adapts' class advisor, # this will effect the global adaptation manager and hence may # interfere with any other tests that rely on it (all of the tests # in this package use a separate adaptation manager so there should # be no clashes here ;^). # # 'adapts' is also deprecated, so expect a warning message when you # run the tests. class BazToIFooAdapter(Adapter): adapts(Baz, IFoo) baz = Baz() bar = Bar() bar.foo = adapt(baz, IFoo) self.assertEqual(bar.foo.adaptee, baz) return #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/test_adaptation_offer.py000066400000000000000000000026001300633736300245430ustar00rootroot00000000000000""" Test the adaptation offers. """ import sys from traits.adaptation.adaptation_offer import AdaptationOffer from traits.testing.unittest_tools import unittest class TestAdaptationOffer(unittest.TestCase): """ Test the adaptation offers. """ def test_lazy_loading(self): LAZY_EXAMPLES = 'traits.adaptation.tests.lazy_examples' if LAZY_EXAMPLES in sys.modules: del sys.modules[LAZY_EXAMPLES] offer = AdaptationOffer( factory =(LAZY_EXAMPLES + '.IBarToIFoo'), from_protocol =(LAZY_EXAMPLES + '.IBar'), to_protocol =(LAZY_EXAMPLES + '.IFoo'), ) self.assertNotIn(LAZY_EXAMPLES, sys.modules) factory = offer.factory self.assertIn(LAZY_EXAMPLES, sys.modules) from traits.adaptation.tests.lazy_examples import IBarToIFoo self.assertIs(factory, IBarToIFoo) del sys.modules[LAZY_EXAMPLES] from_protocol = offer.from_protocol from traits.adaptation.tests.lazy_examples import IBar self.assertIs(from_protocol, IBar) del sys.modules[LAZY_EXAMPLES] to_protocol = offer.to_protocol from traits.adaptation.tests.lazy_examples import IFoo self.assertIs(to_protocol, IFoo) if __name__ == '__main__': unittest.main() #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/test_adapter.py000066400000000000000000000027231300633736300226640ustar00rootroot00000000000000""" Test the Adapter class. """ from traits.api import on_trait_change from traits.adaptation.api import Adapter from traits.testing.unittest_tools import unittest class TestAdapter(unittest.TestCase): """ Test the Adapter class. """ #### Tests ################################################################# def test_initializing_adaptee(self): # Regression test: The `adaptee` trait used to be initialized after # all other traits, which caused "post_init" listeners to be # incorrectly triggered. class FooAdapter(Adapter): # True if a trait change notification for `adaptee` is fired. adaptee_notifier_called = False # True if a post-init trait change notification for `adaptee` # is fired. post_init_notifier_called = False @on_trait_change('adaptee', post_init=True) def check_that_adaptee_start_can_be_accessed(self): self.post_init_notifier_called = True @on_trait_change('adaptee') def check_that_adaptee_change_is_notified(self): self.adaptee_notifier_called = True foo_adapter = FooAdapter(adaptee='1234') self.assertEqual(foo_adapter.adaptee_notifier_called, True) self.assertEqual(foo_adapter.post_init_notifier_called, False) if __name__ == '__main__': unittest.main() #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/test_cached_adapter_factory.py000066400000000000000000000077561300633736300257150ustar00rootroot00000000000000""" Test the cached adapter factory. """ import sys import traits.adaptation.tests.interface_examples from traits.adaptation.api import AdaptationManager from traits.adaptation.cached_adapter_factory import CachedAdapterFactory from traits.testing.unittest_tools import unittest class TestCachedAdapterFactory(unittest.TestCase): """ Test the cached adapter factory. """ examples = traits.adaptation.tests.interface_examples #### 'TestCase' protocol ################################################## def setUp(self): """ Prepares the test fixture before each test method is called. """ self.adaptation_manager = AdaptationManager() return def tearDown(self): """ Called immediately after each test method has been called. """ return #### Tests ################################################################# def test_cached_adapters(self): ex = self.examples factory = CachedAdapterFactory(factory=ex.EditorToIPrintable) self.adaptation_manager.register_factory( factory = factory, from_protocol = ex.Editor, to_protocol = ex.IPrintable ) editor = ex.Editor() adapter_1 = self.adaptation_manager.adapt(editor, ex.IPrintable) self.assertIsNotNone(adapter_1) self.assertIs(type(adapter_1), ex.EditorToIPrintable) adapter_2 = self.adaptation_manager.adapt(editor, ex.IPrintable) self.assertIsNotNone(adapter_2) self.assertIs(type(adapter_2), ex.EditorToIPrintable) self.assertIs(adapter_1, adapter_2) return @unittest.skip("Cache cleaning is broken: see GitHub issue #169") def test_cached_adapters_should_be_cleaned_up(self): ex = self.examples factory = CachedAdapterFactory(factory=ex.EditorToIPrintable) self.adaptation_manager.register_factory( factory = factory, from_protocol = ex.Editor, to_protocol = ex.IPrintable ) editor = ex.Editor() adapter_1 = self.adaptation_manager.adapt(editor, ex.IPrintable) self.assertIsNotNone(adapter_1) self.assertIs(type(adapter_1), ex.EditorToIPrintable) del adapter_1 del editor self.assertTrue(factory.is_empty) return def test_cached_adapters_with_lazy_loaded_factory(self): LAZY_EXAMPLES = 'traits.adaptation.tests.lazy_examples' if LAZY_EXAMPLES in sys.modules: del sys.modules[LAZY_EXAMPLES] factory = CachedAdapterFactory(factory=LAZY_EXAMPLES + '.IBarToIFoo') self.adaptation_manager.register_factory( factory = factory, from_protocol = LAZY_EXAMPLES + '.IBar', to_protocol = LAZY_EXAMPLES + '.IFoo', ) self.assertNotIn(LAZY_EXAMPLES, sys.modules) # The *actual* factory is loaded on-demand. bogus = factory.factory self.assertIn(LAZY_EXAMPLES, sys.modules) return @unittest.skip("Cache cleaning is broken: see GitHub issue #169") def test_cached_adapter_that_was_garbage_collected(self): ex = self.examples factory = CachedAdapterFactory(factory=ex.EditorToIPrintable) self.adaptation_manager.register_factory( factory = factory, from_protocol = ex.Editor, to_protocol = ex.IPrintable ) editor = ex.Editor() adapter_1 = self.adaptation_manager.adapt(editor, ex.IPrintable) self.assertIs(type(adapter_1), ex.EditorToIPrintable) adapter_1.marker = 'marker' del adapter_1 adapter_2 = self.adaptation_manager.adapt(editor, ex.IPrintable) self.assertIsNotNone(adapter_2) self.assertTrue(hasattr(adapter_2, 'marker')) del adapter_2 del editor self.assertTrue(factory.is_empty) if __name__ == '__main__': unittest.main() #### EOF ###################################################################### traits-4.6.0/traits/adaptation/tests/test_global_adaptation_manager.py000066400000000000000000000076771300633736300264170ustar00rootroot00000000000000""" Test the setting/getting/resetting/using the global adaptation manager. """ from traits.adaptation.api import adapt, AdaptationError, AdaptationManager, \ AdaptationOffer, get_global_adaptation_manager, provides_protocol, \ register_factory, register_provides, register_offer, \ reset_global_adaptation_manager, set_global_adaptation_manager, \ supports_protocol import traits.adaptation.tests.abc_examples from traits.testing.unittest_tools import unittest class TestGlobalAdaptationManager(unittest.TestCase): """ Test the setting/getting/resetting/using the global adaptation manager. """ #: Class attribute pointing at the module containing the example data examples = traits.adaptation.tests.abc_examples #### 'TestCase' protocol ################################################## def setUp(self): """ Prepares the test fixture before each test method is called. """ reset_global_adaptation_manager() #### Tests ################################################################ def test_reset_adaptation_manager(self): ex = self.examples adaptation_manager = get_global_adaptation_manager() # UKStandard->EUStandard. adaptation_manager.register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard, ) # Create a UKPlug. uk_plug = ex.UKPlug() reset_global_adaptation_manager() adaptation_manager = get_global_adaptation_manager() with self.assertRaises(AdaptationError): adaptation_manager.adapt(uk_plug, ex.EUStandard) def test_set_adaptation_manager(self): ex = self.examples adaptation_manager = AdaptationManager() # UKStandard->EUStandard. adaptation_manager.register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) # Create a UKPlug. uk_plug = ex.UKPlug() set_global_adaptation_manager(adaptation_manager) global_adaptation_manager = get_global_adaptation_manager() eu_plug = global_adaptation_manager.adapt(uk_plug, ex.EUStandard) self.assertIsNotNone(eu_plug) self.assertIsInstance(eu_plug, ex.UKStandardToEUStandard) def test_global_convenience_functions(self): ex = self.examples # Global `register_factory`. register_factory( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) uk_plug = ex.UKPlug() # Global `adapt`. eu_plug = adapt(uk_plug, ex.EUStandard) self.assertIsNotNone(eu_plug) self.assertIsInstance(eu_plug, ex.UKStandardToEUStandard) # Global `provides_protocol`. self.assertTrue(provides_protocol(ex.UKPlug, ex.UKStandard)) # Global `supports_protocol`. self.assertTrue(supports_protocol(uk_plug, ex.EUStandard)) def test_global_register_provides(self): from traits.api import Interface class IFoo(Interface): pass obj = {} # Global `register_provides`. register_provides(dict, IFoo) self.assertEqual(obj, adapt(obj, IFoo)) def test_global_register_offer(self): ex = self.examples offer = AdaptationOffer( factory = ex.UKStandardToEUStandard, from_protocol = ex.UKStandard, to_protocol = ex.EUStandard ) # Global `register_offer`. register_offer(offer) uk_plug = ex.UKPlug() eu_plug = adapt(uk_plug, ex.EUStandard) self.assertIsNotNone(eu_plug) self.assertIsInstance(eu_plug, ex.UKStandardToEUStandard) if __name__ == '__main__': unittest.main() #### EOF ###################################################################### traits-4.6.0/traits/adapter.py000066400000000000000000000022641300633736300163370ustar00rootroot00000000000000#------------------------------------------------------------------------------- # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: Martin Chilvers # Date: 07/18/2007 # #------------------------------------------------------------------------------- """ An extension to PyProtocols to simplify the declaration of adapters. """ from __future__ import absolute_import import traits.adaptation.adapter from .util.deprecated import deprecated class Adapter(traits.adaptation.adapter.Adapter): @deprecated("use 'Adapter' in 'traits.api' instead") def __init__(self, adaptee, **traits): super(Adapter, self).__init__(adaptee, **traits) adapts = deprecated("use 'adapts' in 'traits.api' instead")( traits.adaptation.adapter.adapts ) #### EOF ###################################################################### traits-4.6.0/traits/api.py000066400000000000000000000117211300633736300154660ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 12/06/2005 # #------------------------------------------------------------------------------ """ Pseudo-package for all of the core symbols from Traits and TraitsUI. Use this module for importing Traits names into your namespace. For example:: from traits.api import HasTraits """ from __future__ import absolute_import from .trait_base import Uninitialized, Undefined, Missing, Self, python_version from .trait_errors import TraitError, TraitNotificationError, DelegationError from .trait_notifiers import (push_exception_handler, pop_exception_handler, TraitChangeNotifyWrapper) from .category import Category from .traits import (CTrait, Trait, Property, TraitFactory, Default, Color, RGBColor, Font) from .trait_types import (Any, Generic, Int, Long, Float, Complex, Str, Title, Unicode, Bytes, Bool, CInt, CLong, CFloat, CComplex, CStr, CUnicode, CBytes, CBool, String, Regex, Code, HTML, Password, Callable, This, self, Function, Method, Module, Python, ReadOnly, Disallow, Constant, Delegate, DelegatesTo, PrototypedFrom, Expression, PythonValue, File, Directory, Range, Enum, Tuple, List, CList, Set, CSet, Dict, Instance, AdaptedTo, AdaptsTo, Event, Button, ToolbarButton, Either, Type, Symbol, WeakRef, Date, Time, false, true, undefined, Supports) from .trait_types import (ListInt, ListFloat, ListStr, ListUnicode, ListComplex, ListBool, ListFunction, ListMethod, ListThis, DictStrAny, DictStrStr, DictStrInt, DictStrLong, DictStrFloat, DictStrBool, DictStrList) try: from .trait_types import Class, ListClass, ListInstance except ImportError: # Python 3 does not have old-style classes anymore, so Class does not exist # interestingly, ListInstance is not equivalent to List(Instance), but # rather only allows old-style instances. pass from .trait_types import (BaseInt, BaseLong, BaseFloat, BaseComplex, BaseStr, BaseUnicode, BaseBytes, BaseBool, BaseCInt, BaseCLong, BaseCFloat, BaseCComplex, BaseCStr, BaseCUnicode, BaseCBool, BaseFile, BaseDirectory, BaseRange, BaseEnum, BaseTuple, BaseInstance) from .trait_types import UUID, ValidatedTuple from .has_traits import (HasTraits, HasStrictTraits, HasPrivateTraits, Interface, SingletonHasTraits, SingletonHasStrictTraits, SingletonHasPrivateTraits, MetaHasTraits, Vetoable, VetoableEvent, implements, traits_super, on_trait_change, cached_property, property_depends_on, provides, isinterface) try: from .has_traits import ABCHasTraits, ABCHasStrictTraits, ABCMetaHasTraits except ImportError: pass from .trait_handlers import (BaseTraitHandler, TraitType, TraitHandler, TraitRange, TraitString, TraitCoerceType, TraitCastType, TraitInstance, ThisClass, TraitClass, TraitFunction, TraitEnum, TraitPrefixList, TraitMap, TraitPrefixMap, TraitCompound, TraitList, TraitListObject, TraitListEvent, TraitSetObject, TraitSetEvent, TraitDict, TraitDictObject, TraitDictEvent, TraitTuple, NO_COMPARE, OBJECT_IDENTITY_COMPARE, RICH_COMPARE) from .trait_value import (BaseTraitValue, TraitValue, SyncValue, TypeValue, DefaultValue) from .adaptation.adapter import Adapter, adapts from .adaptation.adaptation_error import AdaptationError from .adaptation.adaptation_manager import adapt, register_factory, \ register_provides from .trait_numeric import Array, ArrayOrNone, CArray try: from . import has_traits as has_traits #--------------------------------------------------------------------------- # Patch the main traits module with the correct definition for the # ViewElements class: # NOTE: We do this in a try..except block because traits.ui depends on # the pyface module (part of the TraitsGUI package) which may not # necessarily be installed. Not having TraitsGUI means that the 'ui' # features of traits will not work. #--------------------------------------------------------------------------- from traitsui import view_elements has_traits.ViewElements = view_elements.ViewElements #------------------------------------------------------------------------------- # Patch the main traits module with the correct definition for the # ViewElement and ViewSubElement class: #------------------------------------------------------------------------------- has_traits.ViewElement = view_elements.ViewElement except ImportError: pass traits-4.6.0/traits/category.py000066400000000000000000000105351300633736300165340ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 11/06/2004 # #------------------------------------------------------------------------------ """ Adds a "category" capability to Traits-based classes, similar to that provided by the Cocoa (Objective-C) environment for the Macintosh. You can use categories to extend an existing HasTraits class, as an alternative to subclassing. An advantage of categories over subclassing is that you can access the added members on instances of the original class, without having to change them to instances of a subclass. Unlike subclassing, categories do not allow overriding trait attributes. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import from .has_traits import MetaHasTraits, MetaHasTraitsObject #------------------------------------------------------------------------------- # 'MetaCategory' class: #------------------------------------------------------------------------------- class MetaCategory ( MetaHasTraits ): def __new__ ( cls, class_name, bases, class_dict ): # Make sure the correct usage is being applied: if len( bases ) > 2: raise TypeError, \ "Correct usage is: class FooCategory(Category,Foo):" # Process any traits-related information in the class dictionary: MetaCategoryObject( cls, class_name, bases, class_dict, True ) # Move all remaining items in our class dictionary to the base class's # dictionary: if len( bases ) == 2: category_class = bases[1] for name, value in class_dict.items(): if not hasattr( category_class, name ): setattr( category_class, name, value ) del class_dict[ name ] # Finish building the class using the updated class dictionary: return type.__new__( cls, class_name, bases, class_dict ) #------------------------------------------------------------------------------- # 'MetaCategoryObject' class: #------------------------------------------------------------------------------- class MetaCategoryObject ( MetaHasTraitsObject ): #--------------------------------------------------------------------------- # Adds the traits meta-data to the class: #--------------------------------------------------------------------------- def add_traits_meta_data ( self, bases, class_dict, base_traits, class_traits, instance_traits, prefix_traits, listeners, view_elements ): if len( bases ) == 2: # Update the class and each of the existing subclasses: bases[1]._add_trait_category( base_traits, class_traits, instance_traits, prefix_traits, listeners, view_elements ) else: MetaHasTraitsObject.add_traits_meta_data( self, bases, class_dict, base_traits, class_traits, instance_traits, prefix_traits, listeners, view_elements ) #------------------------------------------------------------------------------- # 'Category' class: #------------------------------------------------------------------------------- class Category ( object ): """ Used for defining "category" extensions to existing classes. To define a class as a category, specify "Category," followed by the name of the base class name in the base class list. The following example demonstrates defining a category:: from traits.api import HasTraits, Str, Category class Base(HasTraits): x = Str("Base x") y = Str("Base y") class BaseExtra(Category, Base): z = Str("BaseExtra z") """ __metaclass__ = MetaCategory traits-4.6.0/traits/ctraits.c000066400000000000000000005403461300633736300161720ustar00rootroot00000000000000/****************************************************************************** * * Description: C based implementation of the Traits package * * Copyright (c) 2005, Enthought, Inc. * All rights reserved. * * This software is provided without warranty under the terms of the BSD * license included in enthought/LICENSE.txt and may be redistributed only * under the conditions described in the aforementioned license. The license * is also available online at http://www.enthought.com/licenses/BSD.txt * * Thanks for using Enthought open source! * * Author: David C. Morrill * Date: 06/15/2004 * ******************************************************************************/ /*----------------------------------------------------------------------------- | Includes: +----------------------------------------------------------------------------*/ #include "Python.h" #include "structmember.h" #include "py2to3.h" /*----------------------------------------------------------------------------- | Constants: +----------------------------------------------------------------------------*/ static PyObject * class_traits; /* == "__class_traits__" */ static PyObject * listener_traits; /* == "__listener_traits__" */ static PyObject * editor_property; /* == "editor" */ static PyObject * class_prefix; /* == "__prefix__" */ static PyObject * trait_added; /* == "trait_added" */ static PyObject * empty_tuple; /* == () */ static PyObject * empty_dict; /* == {} */ static PyObject * Undefined; /* Global 'Undefined' value */ static PyObject * Uninitialized; /* Global 'Uninitialized' value */ static PyObject * TraitError; /* TraitError exception */ static PyObject * DelegationError; /* DelegationError exception */ static PyObject * TraitListObject; /* TraitListObject class */ static PyObject * TraitSetObject; /* TraitSetObject class */ static PyObject * TraitDictObject; /* TraitDictObject class */ static PyObject * TraitValue; /* TraitValue class */ static PyObject * adapt; /* PyProtocols 'adapt' function */ static PyObject * validate_implements; /* 'validate implementation' function */ static PyObject * is_callable; /* Marker for 'callable' value */ static PyObject * _HasTraits_monitors; /* Object creation monitors. */ static PyObject * _trait_notification_handler; /* User supplied trait */ /* notification handler (intended for use by debugging tools) */ static PyTypeObject * ctrait_type; /* Python-level CTrait type reference */ /*----------------------------------------------------------------------------- | Macro definitions: +----------------------------------------------------------------------------*/ /* The following macro is automatically defined in Python 2.4 and later: */ #ifndef Py_VISIT #define Py_VISIT(op) \ do { \ if (op) { \ int vret = visit((PyObject *)(op), arg); \ if (vret) return vret; \ } \ } while (0) #endif /* The following macro is automatically defined in Python 2.4 and later: */ #ifndef Py_CLEAR #define Py_CLEAR(op) \ do { \ if (op) { \ PyObject *tmp = (PyObject *)(op); \ (op) = NULL; \ Py_DECREF(tmp); \ } \ } while (0) #endif #define DEFERRED_ADDRESS(ADDR) NULL #define PyTrait_CheckExact(op) ((op)->ob_type == ctrait_type) #define PyHasTraits_Check(op) PyObject_TypeCheck(op, &has_traits_type) #define PyHasTraits_CheckExact(op) ((op)->ob_type == &has_traits_type) /* Trait method related: */ #if PY_MAJOR_VERSION < 3 #define TP_DESCR_GET(t) \ (PyType_HasFeature(t, Py_TPFLAGS_HAVE_CLASS) ? (t)->tp_descr_get : NULL) #else #define TP_DESCR_GET(t) \ ((t)->tp_descr_get) #endif /* Notification related: */ #define has_notifiers(tnotifiers,onotifiers) \ ((((tnotifiers) != NULL) && (PyList_GET_SIZE((tnotifiers))>0)) || \ (((onotifiers) != NULL) && (PyList_GET_SIZE((onotifiers))>0))) /* Python version dependent macros: */ #if ( (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION < 3) ) #define PyMODINIT_FUNC void #define PyDoc_VAR(name) static char name[] #define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) #ifdef WITH_DOC_STRINGS #define PyDoc_STR(str) str #else #define PyDoc_STR(str) "" #endif #endif #if (PY_VERSION_HEX < 0x02050000) typedef int Py_ssize_t; #endif /*----------------------------------------------------------------------------- | Forward declarations: +----------------------------------------------------------------------------*/ static PyTypeObject trait_type; static PyTypeObject has_traits_type; /*----------------------------------------------------------------------------- | 'ctraits' module doc string: +----------------------------------------------------------------------------*/ PyDoc_STRVAR( ctraits__doc__, "The ctraits module defines the CHasTraits and CTrait C extension types that\n" "define the core performance oriented portions of the Traits package." ); /*----------------------------------------------------------------------------- | HasTraits behavior modification flags: +----------------------------------------------------------------------------*/ /* Object has been initialized: */ #define HASTRAITS_INITED 0x00000001 /* Do not send notifications when a trait changes value: */ #define HASTRAITS_NO_NOTIFY 0x00000002 /* Requests that no event notifications be sent when this object is assigned to a trait: */ #define HASTRAITS_VETO_NOTIFY 0x00000004 /*----------------------------------------------------------------------------- | 'CHasTraits' instance definition: | | Note: traits are normally stored in the type's dictionary, but are added to | the instance's traits dictionary 'trait_dict' when the traits are defined | dynamically or 'on_trait_change' is called on an instance of the trait. | | All 'anytrait_changed' notification handlers are stored in the instance's | 'notifiers' list. +----------------------------------------------------------------------------*/ typedef struct { PyObject_HEAD /* Standard Python object header */ PyDictObject * ctrait_dict; /* Class traits dictionary */ PyDictObject * itrait_dict; /* Instance traits dictionary */ PyListObject * notifiers; /* List of 'any trait changed' notification handlers */ int flags; /* Behavior modification flags */ PyObject * obj_dict; /* Object attribute dictionary ('__dict__') */ /* NOTE: 'obj_dict' field MUST be last field */ } has_traits_object; static int call_notifiers ( PyListObject *, PyListObject *, has_traits_object *, PyObject *, PyObject *, PyObject * new_value ); /*----------------------------------------------------------------------------- | 'CTrait' flag values: +----------------------------------------------------------------------------*/ /* The trait is a Property: */ #define TRAIT_PROPERTY 0x00000001 /* Should the delegate be modified (or the original object)? */ #define TRAIT_MODIFY_DELEGATE 0x00000002 /* Should a simple object identity test be performed (or a rich compare)? */ #define TRAIT_OBJECT_IDENTITY 0x00000004 /* Make 'setattr' store the original unvalidated value */ #define TRAIT_SETATTR_ORIGINAL_VALUE 0x00000008 /* Send the 'post_setattr' method the original unvalidated value */ #define TRAIT_POST_SETATTR_ORIGINAL_VALUE 0x00000010 /* Can a 'TraitValue' be assigned to override the trait definition? */ #define TRAIT_VALUE_ALLOWED 0x00000020 /* Is this trait a special 'TraitValue' trait that uses a property? */ #define TRAIT_VALUE_PROPERTY 0x00000040 /* Does this trait have an associated 'mapped' trait? */ #define TRAIT_IS_MAPPED 0x00000080 /* Should any old/new value test be performed before generating notifications? */ #define TRAIT_NO_VALUE_TEST 0x00000100 /*----------------------------------------------------------------------------- | 'CTrait' instance definition: +----------------------------------------------------------------------------*/ typedef struct _trait_object a_trait_object; typedef PyObject * (*trait_getattr)( a_trait_object *, has_traits_object *, PyObject * ); typedef int (*trait_setattr)( a_trait_object *, a_trait_object *, has_traits_object *, PyObject *, PyObject * ); typedef int (*trait_post_setattr)( a_trait_object *, has_traits_object *, PyObject *, PyObject * ); typedef PyObject * (*trait_validate)( a_trait_object *, has_traits_object *, PyObject *, PyObject * ); typedef PyObject * (*delegate_attr_name_func)( a_trait_object *, has_traits_object *, PyObject * ); typedef struct _trait_object { PyObject_HEAD /* Standard Python object header */ int flags; /* Flag bits */ trait_getattr getattr; /* Get trait value handler */ trait_setattr setattr; /* Set trait value handler */ trait_post_setattr post_setattr; /* Optional post 'setattr' handler */ PyObject * py_post_setattr; /* Python-based post 'setattr' hndlr */ trait_validate validate; /* Validate trait value handler */ PyObject * py_validate; /* Python-based validate value handler */ int default_value_type; /* Type of default value: see the 'default_value_for' function */ PyObject * default_value; /* Default value for trait */ PyObject * delegate_name; /* Optional delegate name */ /* Also used for 'property get' */ PyObject * delegate_prefix; /* Optional delegate prefix */ /* Also used for 'property set' */ delegate_attr_name_func delegate_attr_name; /* Optional routine to return*/ /* the computed delegate attribute name */ PyListObject * notifiers; /* Optional list of notification handlers */ PyObject * handler; /* Associated trait handler object */ /* NOTE: The 'obj_dict' field MUST be last */ PyObject * obj_dict; /* Standard Python object dictionary */ } trait_object; /* Forward declarations: */ static void trait_clone ( trait_object *, trait_object * ); static PyObject * has_traits_getattro ( has_traits_object * obj, PyObject * name ); static int has_traits_setattro ( has_traits_object * obj, PyObject * name, PyObject * value ); static PyObject * get_trait ( has_traits_object * obj, PyObject * name, int instance ); static int trait_property_changed ( has_traits_object * obj, PyObject * name, PyObject * old_value, PyObject * new_value ); static int setattr_event ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ); static int setattr_disallow ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ); /*----------------------------------------------------------------------------- | Raise a TraitError: +----------------------------------------------------------------------------*/ static PyObject * raise_trait_error ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; /* Clear any current exception. We are handling it by raising * a TraitError. */ PyErr_Clear(); result = PyObject_CallMethod( trait->handler, "error", "(OOO)", obj, name, value ); Py_XDECREF( result ); return NULL; } /*----------------------------------------------------------------------------- | Raise a fatal trait error: +----------------------------------------------------------------------------*/ static int fatal_trait_error ( void ) { PyErr_SetString( TraitError, "Non-trait found in trait dictionary" ); return -1; } /*----------------------------------------------------------------------------- | Raise an "attribute is not a string" error: +----------------------------------------------------------------------------*/ static int invalid_attribute_error ( PyObject * name ) { #if PY_MAJOR_VERSION >= 3 const char* fmt = "attribute name must be an instance of . " "Got %R (%.200s)."; PyErr_Format(PyExc_TypeError, fmt, name, name->ob_type->tp_name); #else // Python 2.6 doesn't support %R in PyErr_Format, so we compute and // insert the repr explicitly. const char* fmt = "attribute name must be an instance of . " "Got %.200s (%.200s)."; PyObject *obj_repr; obj_repr = PyObject_Repr(name); if ( obj_repr == NULL ) { return -1; } PyErr_Format(PyExc_TypeError, fmt, PyString_AsString(obj_repr), name->ob_type->tp_name); Py_DECREF( obj_repr ); #endif return -1; } /*----------------------------------------------------------------------------- | Raise an "invalid trait definition" error: +----------------------------------------------------------------------------*/ static int bad_trait_error ( void ) { PyErr_SetString( TraitError, "Invalid argument to trait constructor." ); return -1; } /*----------------------------------------------------------------------------- | Raise an "cant set items error" error: +----------------------------------------------------------------------------*/ static PyObject * cant_set_items_error ( void ) { PyErr_SetString( TraitError, "Can not set a collection's '_items' trait." ); return NULL; } /*----------------------------------------------------------------------------- | Raise an "invalid trait definition" error: +----------------------------------------------------------------------------*/ static int bad_trait_value_error ( void ) { PyErr_SetString( TraitError, "Result of 'as_ctrait' method was not a 'CTraits' instance." ); return -1; } /*----------------------------------------------------------------------------- | Raise an invalid delegate error: +----------------------------------------------------------------------------*/ static int bad_delegate_error ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( DelegationError, "The '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object" " delegates to an attribute which is not a defined trait.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise an invalid delegate error: +----------------------------------------------------------------------------*/ static int bad_delegate_error2 ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( DelegationError, "The '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object" " has a delegate which does not have traits.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise a delegation recursion error: +----------------------------------------------------------------------------*/ static int delegation_recursion_error ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( DelegationError, "Delegation recursion limit exceeded while setting" " the '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } static int delegation_recursion_error2 ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( DelegationError, "Delegation recursion limit exceeded while getting" " the definition of" " the '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise an attempt to delete read-only attribute error: +----------------------------------------------------------------------------*/ static int delete_readonly_error ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( TraitError, "Cannot delete the read only '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise an attempt to set a read-only attribute error: +----------------------------------------------------------------------------*/ static int set_readonly_error ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( TraitError, "Cannot modify the read only '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise an attempt to set an undefined attribute error: +----------------------------------------------------------------------------*/ static int set_disallow_error ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( TraitError, "Cannot set the undefined '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise an attempt to delete a property error: +----------------------------------------------------------------------------*/ static int set_delete_property_error ( has_traits_object * obj, PyObject * name ) { if ( !Py2to3_SimpleString_Check( name ) ) { return invalid_attribute_error( name ); } PyErr_Format( TraitError, "Cannot delete the '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " property of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } /*----------------------------------------------------------------------------- | Raise an undefined attribute error: +----------------------------------------------------------------------------*/ static void unknown_attribute_error ( has_traits_object * obj, PyObject * name ) { PyErr_Format( PyExc_AttributeError, "'%.50s' object has no attribute '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'", Py_TYPE(obj)->tp_name, Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ) ); } /*----------------------------------------------------------------------------- | Raise a '__dict__' must be set to a dictionary error: +----------------------------------------------------------------------------*/ static int dictionary_error ( void ) { PyErr_SetString( PyExc_TypeError, "__dict__ must be set to a dictionary." ); return -1; } /*----------------------------------------------------------------------------- | Gets/Sets a possibly NULL (or callable) value: +----------------------------------------------------------------------------*/ static PyObject * get_callable_value ( PyObject * value ) { PyObject * tuple, * temp; if ( value == NULL ) { value = Py_None; Py_INCREF( value ); } else if ( PyCallable_Check( value ) ) { value = is_callable; Py_INCREF( value ); } else if ( PyTuple_Check( value ) && ( PyTuple_GET_SIZE( value ) >= 3 ) && ( Py2to3_PyNum_AsLong( PyTuple_GET_ITEM( value, 0 ) ) == 10) ) { tuple = PyTuple_New( 3 ); if ( tuple != NULL ) { PyTuple_SET_ITEM( tuple, 0, temp = PyTuple_GET_ITEM( value, 0 ) ); Py_INCREF( temp ); PyTuple_SET_ITEM( tuple, 1, temp = PyTuple_GET_ITEM( value, 1 ) ); Py_INCREF( temp ); PyTuple_SET_ITEM( tuple, 2, is_callable ); Py_INCREF( is_callable ); value = tuple; } else { value = NULL; } } else { Py_INCREF( value ); } return value; } static PyObject * get_value ( PyObject * value ) { if ( value == NULL ) value = Py_None; Py_INCREF( value ); return value; } static int set_value ( PyObject ** field, PyObject * value ) { Py_INCREF( value ); Py_XDECREF( *field ); *field = value; return 0; } /*----------------------------------------------------------------------------- | Returns the result of calling a specified 'class' object with 1 argument: +----------------------------------------------------------------------------*/ static PyObject * call_class ( PyObject * class, trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args = PyTuple_New( 4 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, trait->handler ); PyTuple_SET_ITEM( args, 1, (PyObject *) obj ); PyTuple_SET_ITEM( args, 2, name ); PyTuple_SET_ITEM( args, 3, value ); Py_INCREF( trait->handler ); Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( value ); result = PyObject_Call( class, args, NULL ); Py_DECREF( args ); return result; } /*----------------------------------------------------------------------------- | Attempts to get the value of a key in a 'known to be a dictionary' object: +----------------------------------------------------------------------------*/ static PyObject * dict_getitem ( PyDictObject * dict, PyObject *key ) { #if !defined(Py_LIMITED_API) && (PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 3) Py_hash_t hash; #endif assert( PyDict_Check( dict ) ); #if !defined(Py_LIMITED_API) && (PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 3) hash = Py2to3_GetHash_wCache( key ); if ( hash == -1 ) { PyErr_Clear(); return NULL; } return (dict->ma_lookup)( dict, key, hash )->me_value; #else return PyDict_GetItem((PyObject *)dict,key); #endif } /*----------------------------------------------------------------------------- | Gets the definition of the matching prefix based trait for a specified name: | | - This should always return a trait definition unless a fatal Python error | occurs. | - The bulk of the work is delegated to a Python implemented method because | the implementation is complicated in C and does not need to be executed | very often relative to other operations. | +----------------------------------------------------------------------------*/ static trait_object * get_prefix_trait ( has_traits_object * obj, PyObject * name, int is_set ) { PyObject * trait = PyObject_CallMethod( (PyObject *) obj, "__prefix_trait__", "(Oi)", name, is_set ); if ( trait != NULL ) { assert( obj->ctrait_dict != NULL ); PyDict_SetItem( (PyObject *) obj->ctrait_dict, name, trait ); Py_DECREF( trait ); if ( has_traits_setattro( obj, trait_added, name ) < 0 ) return NULL; trait = get_trait( obj, name, 0 ); Py_DECREF( trait ); } return (trait_object *) trait; } /*----------------------------------------------------------------------------- | Assigns a special TraitValue to a specified trait attribute: +----------------------------------------------------------------------------*/ static int setattr_value ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyDictObject * dict; PyObject * trait_new, * result, * obj_dict; PyObject * trait_old = NULL; PyObject * value_old = NULL; trait_new = PyObject_CallMethod( value, "as_ctrait", "(O)", trait ); if ( trait_new == NULL ) goto error2; if ( (trait_new != Py_None) && (!PyTrait_CheckExact( trait_new )) ) { Py_DECREF( trait_new ); return bad_trait_value_error(); } dict = obj->itrait_dict; if ( (dict != NULL) && ((trait_old = dict_getitem( dict, name )) != NULL) && ((((trait_object *) trait_old)->flags & TRAIT_VALUE_PROPERTY) != 0) ) { result = PyObject_CallMethod( trait_old, "_unregister", "(OO)", obj, name ); if ( result == NULL ) goto error1; Py_DECREF( result ); } if ( trait_new == Py_None ) { if ( trait_old != NULL ) { PyDict_DelItem( (PyObject *) dict, name ); } goto success; } if ( dict == NULL ) { obj->itrait_dict = dict = (PyDictObject *) PyDict_New(); if ( dict == NULL ) goto error1; } if ( (((trait_object *) trait_new)->flags & TRAIT_VALUE_PROPERTY) != 0 ) { if ( (value_old = has_traits_getattro( obj, name )) == NULL ) goto error1; obj_dict = obj->obj_dict; if ( obj_dict != NULL ) PyDict_DelItem( obj_dict, name ); } if ( PyDict_SetItem( (PyObject *) dict, name, trait_new ) < 0 ) goto error0; if ( (((trait_object *) trait_new)->flags & TRAIT_VALUE_PROPERTY) != 0 ) { result = PyObject_CallMethod( trait_new, "_register", "(OO)", obj, name ); if ( result == NULL ) goto error0; Py_DECREF( result ); if ( trait_property_changed( obj, name, value_old, NULL ) ) goto error0; Py_DECREF( value_old ); } success: Py_DECREF( trait_new ); return 0; error0: Py_XDECREF( value_old ); error1: Py_DECREF( trait_new ); error2: return -1; } /*----------------------------------------------------------------------------- | Handles the 'setattr' operation on a 'CHasTraits' instance: +----------------------------------------------------------------------------*/ static int has_traits_setattro ( has_traits_object * obj, PyObject * name, PyObject * value ) { trait_object * trait; if ( (obj->itrait_dict == NULL) || ((trait = (trait_object *) dict_getitem( obj->itrait_dict, name )) == NULL) ) { trait = (trait_object *) dict_getitem( obj->ctrait_dict, name ); if ( (trait == NULL) && ((trait = get_prefix_trait( obj, name, 1 )) == NULL) ) return -1; } if ( ((trait->flags & TRAIT_VALUE_ALLOWED) != 0) && (PyObject_IsInstance( value, TraitValue ) > 0) ) { return setattr_value( trait, obj, name, value ); } return trait->setattr( trait, trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Allocates a CTrait instance: +----------------------------------------------------------------------------*/ PyObject * has_traits_new ( PyTypeObject * type, PyObject * args, PyObject * kwds ) { // Call PyBaseObject_Type.tp_new to do the actual construction. // This allows things like ABCMeta machinery to work correctly // which is implemented at the C level. has_traits_object * obj = (has_traits_object *) PyBaseObject_Type.tp_new(type, empty_tuple, empty_dict); if ( obj != NULL ) { if (type->tp_dict == NULL) { PyErr_SetString(PyExc_RuntimeError, "No tp_dict"); return NULL; } obj->ctrait_dict = (PyDictObject *) PyDict_GetItem( type->tp_dict, class_traits ); if (obj->ctrait_dict == NULL) { PyErr_SetString(PyExc_RuntimeError, "No ctrait_dict"); return NULL; } if (!PyDict_Check( (PyObject *) obj->ctrait_dict ) ) { PyErr_SetString(PyExc_RuntimeError, "ctrait_dict not a dict"); return NULL; } Py_INCREF( obj->ctrait_dict ); } return (PyObject *) obj; } int has_traits_init ( PyObject * obj, PyObject * args, PyObject * kwds ) { PyObject * key; PyObject * value; PyObject * klass; PyObject * handler; PyObject * handler_args; int n; int has_listeners; Py_ssize_t i = 0; /* Make sure no non-keyword arguments were specified: */ if ( !PyArg_ParseTuple( args, "" ) ) return -1; /* Make sure all of the object's listeners have been set up: */ has_listeners = (PyMapping_Size( PyDict_GetItem( obj->ob_type->tp_dict, listener_traits ) ) > 0); if ( has_listeners ) { value = PyObject_CallMethod( obj, "_init_trait_listeners", "()" ); if ( value == NULL ) return -1; Py_DECREF( value ); } /* Set any traits specified in the constructor: */ if ( kwds != NULL ) { while ( PyDict_Next( kwds, &i, &key, &value ) ) { if ( has_traits_setattro( (has_traits_object *) obj, key, value ) == -1 ) return -1; } } /* Make sure all post constructor argument assignment listeners have been set up: */ if ( has_listeners ) { value = PyObject_CallMethod( obj, "_post_init_trait_listeners", "()" ); if ( value == NULL ) return -1; Py_DECREF( value ); } /* Notify any interested monitors that a new object has been created: */ for ( i = 0, n = PyList_GET_SIZE( _HasTraits_monitors ); i < n; i++ ) { value = PyList_GET_ITEM( _HasTraits_monitors, i ); assert( PyTuple_Check( value ) ); assert( PyTuple_GET_SIZE( value ) == 2 ); klass = PyTuple_GET_ITEM( value, 0 ); handler = PyTuple_GET_ITEM( value, 1 ); if ( PyObject_IsInstance( obj, klass ) > 0 ) { handler_args = PyTuple_New( 1 ); PyTuple_SetItem( handler_args, 0, obj ); Py_INCREF( obj ); PyObject_Call( handler, handler_args, NULL ); Py_DECREF( handler_args ); } } /* Call the 'traits_init' method to finish up initialization: */ value = PyObject_CallMethod( obj, "traits_init", "()" ); if ( value == NULL ) return -1; Py_DECREF( value ); /* Indicate that the object has finished being initialized: */ ((has_traits_object *) obj)->flags |= HASTRAITS_INITED; return 0; } /*----------------------------------------------------------------------------- | Object clearing method: +----------------------------------------------------------------------------*/ static int has_traits_clear ( has_traits_object * obj ) { Py_CLEAR( obj->ctrait_dict ); Py_CLEAR( obj->itrait_dict ); Py_CLEAR( obj->notifiers ); Py_CLEAR( obj->obj_dict ); return 0; } /*----------------------------------------------------------------------------- | Deallocates an unused 'CHasTraits' instance: +----------------------------------------------------------------------------*/ static void has_traits_dealloc ( has_traits_object * obj ) { PyObject_GC_UnTrack(obj); Py_TRASHCAN_SAFE_BEGIN(obj); has_traits_clear( obj ); Py_TYPE(obj)->tp_free( (PyObject *) obj ); Py_TRASHCAN_SAFE_END(obj); } /*----------------------------------------------------------------------------- | Garbage collector traversal method: +----------------------------------------------------------------------------*/ static int has_traits_traverse ( has_traits_object * obj, visitproc visit, void * arg ) { Py_VISIT( obj->ctrait_dict ); Py_VISIT( obj->itrait_dict ); Py_VISIT( obj->notifiers ); Py_VISIT( obj->obj_dict ); return 0; } /*----------------------------------------------------------------------------- | Handles the 'getattr' operation on a 'CHasTraits' instance: +----------------------------------------------------------------------------*/ static PyObject * has_traits_getattro ( has_traits_object * obj, PyObject * name ) { trait_object * trait; PyObject *value; PyObject *bad_attr_marker; /* The following is a performance hack to short-circuit the normal look-up when the value is in the object's dictionary. */ PyDictObject * dict = (PyDictObject *) obj->obj_dict; if ( dict != NULL ) { assert( PyDict_Check( dict ) ); bad_attr_marker = name; value = Py2to3_GetAttrDictValue(dict, name, bad_attr_marker); // there is a slight performance-hit here: // Py2to3_GetAttrDictValue cannot signal invalid attributes // unambiguously, so we have to reckeck in case the marker value is // returned. Make sure to pick an unlikely marker value. if((value==bad_attr_marker) && !Py2to3_AttrNameCheck(name)) { invalid_attribute_error( name ); return NULL; } if( value != NULL ){ Py_INCREF( value ); return value; } } /* End of performance hack */ if ( ((obj->itrait_dict != NULL) && ((trait = (trait_object *) dict_getitem( obj->itrait_dict, name )) != NULL)) || ((trait = (trait_object *) dict_getitem( obj->ctrait_dict, name )) != NULL) ) { return trait->getattr( trait, obj, name ); } if ( (value = PyObject_GenericGetAttr( (PyObject *) obj, name )) != NULL ) return value; PyErr_Clear(); if ( (trait = get_prefix_trait( obj, name, 0 )) != NULL ) return trait->getattr( trait, obj, name ); return NULL; } /*----------------------------------------------------------------------------- | Returns (and optionally creates) a specified instance or class trait: +----------------------------------------------------------------------------*/ static PyObject * get_trait ( has_traits_object * obj, PyObject * name, int instance ) { int i, n; PyDictObject * itrait_dict; trait_object * trait; trait_object * itrait; PyListObject * notifiers; PyListObject * inotifiers; PyObject * item; /* If there already is an instance specific version of the requested trait, then return it: */ itrait_dict = obj->itrait_dict; if ( itrait_dict != NULL ) { trait = (trait_object *) dict_getitem( itrait_dict, name ); if ( trait != NULL ) { assert( PyTrait_CheckExact( trait ) ); Py_INCREF( trait ); return (PyObject *) trait; } } /* If only an instance trait can be returned (but not created), then return None: */ if ( instance == 1 ) { Py_INCREF( Py_None ); return Py_None; } /* Otherwise, get the class specific version of the trait (creating a trait class version if necessary): */ assert( obj->ctrait_dict != NULL ); trait = (trait_object *) dict_getitem( obj->ctrait_dict, name ); if ( trait == NULL ) { if ( instance == 0 ) { Py_INCREF( Py_None ); return Py_None; } if ( (trait = get_prefix_trait( obj, name, 0 )) == NULL ) return NULL; } assert( PyTrait_CheckExact( trait ) ); /* If an instance specific trait is not needed, return the class trait: */ if ( instance <= 0 ) { Py_INCREF( trait ); return (PyObject *) trait; } /* Otherwise, create an instance trait dictionary if it does not exist: */ if ( itrait_dict == NULL ) { obj->itrait_dict = itrait_dict = (PyDictObject *) PyDict_New(); if ( itrait_dict == NULL ) return NULL; } /* Create a new instance trait and clone the class trait into it: */ itrait = (trait_object *) PyType_GenericAlloc( ctrait_type, 0 ); trait_clone( itrait, trait ); itrait->obj_dict = trait->obj_dict; Py_XINCREF( itrait->obj_dict ); /* Copy the class trait's notifier list into the instance trait: */ if ( (notifiers = trait->notifiers) != NULL ) { n = PyList_GET_SIZE( notifiers ); itrait->notifiers = inotifiers = (PyListObject *) PyList_New( n ); if ( inotifiers == NULL ) return NULL; for ( i = 0; i < n; i++ ) { item = PyList_GET_ITEM( notifiers, i ); PyList_SET_ITEM( inotifiers, i, item ); Py_INCREF( item ); } } /* Add the instance trait to the instance's trait dictionary and return the instance trait if successful: */ if ( PyDict_SetItem( (PyObject *) itrait_dict, name, (PyObject *) itrait ) >= 0 ) return (PyObject *) itrait; /* Otherwise, indicate that an error ocurred updating the dictionary: */ return NULL; } /*----------------------------------------------------------------------------- | Returns (and optionally creates) a specified instance or class trait: | | The legal values for 'instance' are: | 2: Return instance trait (force creation if it does not exist) | 1: Return existing instance trait (do not create) | 0: Return existing instance or class trait (do not create) | -1: Return instance trait or force create class trait (i.e. prefix trait) | -2: Return the base trait (after all delegation has been resolved) +----------------------------------------------------------------------------*/ static PyObject * _has_traits_trait ( has_traits_object * obj, PyObject * args ) { has_traits_object * delegate; has_traits_object * temp_delegate; trait_object * trait; PyObject * name; PyObject * daname; PyObject * daname2; PyObject * dict; int i, instance; /* Parse arguments, which specify the trait name and whether or not an instance specific version of the trait is needed or not: */ if ( !PyArg_ParseTuple( args, "Oi", &name, &instance ) ) return NULL; trait = (trait_object *) get_trait( obj, name, instance ); if ( (instance >= -1) || (trait == NULL) ) return (PyObject *) trait; /* Follow the delegation chain until we find a non-delegated trait: */ delegate = obj; Py_INCREF( delegate ); daname = name; Py_INCREF( daname ); for ( i = 0; ; ) { if ( trait->delegate_attr_name == NULL ) { Py_DECREF( delegate ); Py_DECREF( daname ); return (PyObject *) trait; } dict = delegate->obj_dict; temp_delegate = NULL; if (dict != NULL) { temp_delegate = (has_traits_object *) PyDict_GetItem( dict, trait->delegate_name ); /* PyDict_GetItem returns a borrowed reference, so we need to INCREF. */ Py_XINCREF( temp_delegate ); } if (temp_delegate == NULL) { /* has_traits_getattro returns a new reference, so no need to INCREF. */ temp_delegate = (has_traits_object *) has_traits_getattro( delegate, trait->delegate_name ); } if (temp_delegate == NULL) { break; } Py_DECREF( delegate ); delegate = temp_delegate; if ( !PyHasTraits_Check( delegate ) ) { bad_delegate_error2( obj, name ); break; } daname2 = trait->delegate_attr_name( trait, obj, daname ); Py_DECREF( daname ); daname = daname2; Py_DECREF( trait ); if ( ((delegate->itrait_dict == NULL) || ((trait = (trait_object *) dict_getitem( delegate->itrait_dict, daname )) == NULL)) && ((trait = (trait_object *) dict_getitem( delegate->ctrait_dict, daname )) == NULL) && ((trait = get_prefix_trait( delegate, daname2, 0 )) == NULL) ) { bad_delegate_error( obj, name ); break; } if ( Py_TYPE(trait) != ctrait_type ) { fatal_trait_error(); break; } if ( ++i >= 100 ) { delegation_recursion_error2( obj, name ); break; } Py_INCREF( trait ); } Py_DECREF( delegate ); Py_DECREF( daname ); return NULL; } /*----------------------------------------------------------------------------- | Calls notifiers when a trait 'property' is explicitly changed: +----------------------------------------------------------------------------*/ static int trait_property_changed ( has_traits_object * obj, PyObject * name, PyObject * old_value, PyObject * new_value ) { trait_object * trait; PyListObject * tnotifiers; PyListObject * onotifiers; int null_new_value; int rc = 0; if ( (trait = (trait_object *) get_trait( obj, name, -1 )) == NULL ) return -1; tnotifiers = trait->notifiers; onotifiers = obj->notifiers; Py_DECREF( trait ); if ( has_notifiers( tnotifiers, onotifiers ) ) { null_new_value = (new_value == NULL); if ( null_new_value ) { new_value = has_traits_getattro( obj, name ); if ( new_value == NULL ) return -1; } rc = call_notifiers( tnotifiers, onotifiers, obj, name, old_value, new_value ); if ( null_new_value ) { Py_DECREF( new_value ); } } return rc; } /*----------------------------------------------------------------------------- | Calls notifiers when a trait 'property' is explicitly changed: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_property_changed ( has_traits_object * obj, PyObject * args ) { PyObject * name, * old_value; PyObject * new_value = NULL; /* Parse arguments, which specify the name of the changed trait, the previous value, and the new value: */ if ( !PyArg_ParseTuple( args, "OO|O", &name, &old_value, &new_value ) ) return NULL; if ( trait_property_changed( obj, name, old_value, new_value ) ) return NULL; Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Handles firing a traits 'xxx_items' event: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_items_event ( has_traits_object * obj, PyObject * args ) { PyObject * name; PyObject * event_object; PyObject * event_trait; PyObject * result; trait_object * trait; int can_retry = 1; if ( !PyArg_ParseTuple( args, "OOO", &name, &event_object, &event_trait ) ) return NULL; if ( !PyTrait_CheckExact( event_trait ) ) { bad_trait_value_error(); return NULL; } if ( !Py2to3_AttrNameCheck( name ) ) { invalid_attribute_error( name ); return NULL; } retry: if ( ((obj->itrait_dict == NULL) || ((trait = (trait_object *) dict_getitem( obj->itrait_dict, name )) == NULL)) && ((trait = (trait_object *) dict_getitem( obj->ctrait_dict, name )) == NULL) ) { add_trait: if ( !can_retry ) return cant_set_items_error(); result = PyObject_CallMethod( (PyObject *) obj, "add_trait", "(OO)", name, event_trait ); if ( result == NULL ) return NULL; Py_DECREF( result ); can_retry = 0; goto retry; } if ( trait->setattr == setattr_disallow ) goto add_trait; if ( trait->setattr( trait, trait, obj, name, event_object ) < 0 ) return NULL; Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Enables/Disables trait change notification for the object: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_change_notify ( has_traits_object * obj, PyObject * args ) { int enabled; /* Parse arguments, which specify the new trait notification enabled/disabled state: */ if ( !PyArg_ParseTuple( args, "i", &enabled ) ) return NULL; if ( enabled ) { obj->flags &= (~HASTRAITS_NO_NOTIFY); } else { obj->flags |= HASTRAITS_NO_NOTIFY; } Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Enables/Disables trait change notifications when this object is assigned to | a trait: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_veto_notify ( has_traits_object * obj, PyObject * args ) { int enabled; /* Parse arguments, which specify the new trait notification veto enabled/disabled state: */ if ( !PyArg_ParseTuple( args, "i", &enabled ) ) return NULL; if ( enabled ) { obj->flags |= HASTRAITS_VETO_NOTIFY; } else { obj->flags &= (~HASTRAITS_VETO_NOTIFY); } Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | This method is called at the end of a HasTraits constructor and the | __setstate__ method to perform any final object initialization needed. +----------------------------------------------------------------------------*/ static PyObject * _has_traits_init ( has_traits_object * obj ) { Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Returns whether or not the object has finished being initialized: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_inited ( has_traits_object * obj, PyObject * args ) { int traits_inited = -1; if ( !PyArg_ParseTuple( args, "|i", &traits_inited ) ) return NULL; if ( traits_inited > 0 ) obj->flags |= HASTRAITS_INITED; if ( obj->flags & HASTRAITS_INITED ) { Py_INCREF( Py_True ); return Py_True; } Py_INCREF( Py_False ); return Py_False; } /*----------------------------------------------------------------------------- | Returns the instance trait dictionary: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_instance_traits ( has_traits_object * obj, PyObject * args ) { if ( !PyArg_ParseTuple( args, "" ) ) return NULL; if ( obj->itrait_dict == NULL ) obj->itrait_dict = (PyDictObject *) PyDict_New(); Py_XINCREF( obj->itrait_dict ); return (PyObject *) obj->itrait_dict; } /*----------------------------------------------------------------------------- | Returns (and optionally creates) the anytrait 'notifiers' list: +----------------------------------------------------------------------------*/ static PyObject * _has_traits_notifiers ( has_traits_object * obj, PyObject * args ) { PyObject * result; PyObject * list; int force_create; if ( !PyArg_ParseTuple( args, "i", &force_create ) ) return NULL; result = (PyObject *) obj->notifiers; if ( result == NULL ) { if ( force_create ) { list = PyList_New(0); if (list == NULL) return NULL; obj->notifiers = (PyListObject *)list; result = list; } else { result = Py_None; } } Py_INCREF( result ); return result; } /*----------------------------------------------------------------------------- | Returns the object's instance dictionary: +----------------------------------------------------------------------------*/ static PyObject * get_has_traits_dict ( has_traits_object * obj, void * closure ) { PyObject * obj_dict = obj->obj_dict; if ( obj_dict == NULL ) { obj->obj_dict = obj_dict = PyDict_New(); if ( obj_dict == NULL ) return NULL; } Py_INCREF( obj_dict ); return obj_dict; } /*----------------------------------------------------------------------------- | Sets the object's dictionary: +----------------------------------------------------------------------------*/ static int set_has_traits_dict ( has_traits_object * obj, PyObject * value, void * closure ) { if ( !PyDict_Check( value ) ) return dictionary_error(); return set_value( &obj->obj_dict, value ); } /*----------------------------------------------------------------------------- | 'CHasTraits' instance methods: +----------------------------------------------------------------------------*/ static PyMethodDef has_traits_methods[] = { { "trait_property_changed", (PyCFunction) _has_traits_property_changed, METH_VARARGS, PyDoc_STR( "trait_property_changed(name,old_value[,new_value])" ) }, { "trait_items_event", (PyCFunction) _has_traits_items_event, METH_VARARGS, PyDoc_STR( "trait_items_event(event_trait,name,items_event)" ) }, { "_trait_change_notify", (PyCFunction) _has_traits_change_notify, METH_VARARGS, PyDoc_STR( "_trait_change_notify(boolean)" ) }, { "_trait_veto_notify", (PyCFunction) _has_traits_veto_notify, METH_VARARGS, PyDoc_STR( "_trait_veto_notify(boolean)" ) }, { "traits_init", (PyCFunction) _has_traits_init, METH_NOARGS, PyDoc_STR( "traits_init()" ) }, { "traits_inited", (PyCFunction) _has_traits_inited, METH_VARARGS, PyDoc_STR( "traits_inited([True])" ) }, { "_trait", (PyCFunction) _has_traits_trait, METH_VARARGS, PyDoc_STR( "_trait(name,instance) -> trait" ) }, { "_instance_traits", (PyCFunction) _has_traits_instance_traits, METH_VARARGS, PyDoc_STR( "_instance_traits() -> dict" ) }, { "_notifiers", (PyCFunction) _has_traits_notifiers, METH_VARARGS, PyDoc_STR( "_notifiers(force_create) -> list" ) }, { NULL, NULL }, }; /*----------------------------------------------------------------------------- | 'CHasTraits' property definitions: +----------------------------------------------------------------------------*/ static PyGetSetDef has_traits_properties[] = { { "__dict__", (getter) get_has_traits_dict, (setter) set_has_traits_dict }, { 0 } }; /*----------------------------------------------------------------------------- | 'CHasTraits' type definition: +----------------------------------------------------------------------------*/ static PyTypeObject has_traits_type = { PyVarObject_HEAD_INIT( DEFERRED_ADDRESS( &PyType_Type ), 0) "traits.ctraits.CHasTraits", sizeof( has_traits_object ), 0, (destructor) has_traits_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ (getattrofunc) has_traits_getattro, /* tp_getattro */ (setattrofunc) has_traits_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc) has_traits_traverse, /* tp_traverse */ (inquiry) has_traits_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ has_traits_methods, /* tp_methods */ 0, /* tp_members */ has_traits_properties, /* tp_getset */ DEFERRED_ADDRESS( &PyBaseObject_Type ), /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ sizeof( has_traits_object ) - sizeof( PyObject * ), /* tp_dictoffset */ has_traits_init, /* tp_init */ DEFERRED_ADDRESS( PyType_GenericAlloc ), /* tp_alloc */ has_traits_new /* tp_new */ }; /*----------------------------------------------------------------------------- | Returns the default value associated with a specified trait: +----------------------------------------------------------------------------*/ static PyObject * default_value_for ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyObject * result = NULL, * value, * dv, * kw, * tuple; switch ( trait->default_value_type ) { case 0: case 1: result = trait->default_value; Py_INCREF( result ); break; case 2: result = (PyObject *) obj; Py_INCREF( obj ); break; case 3: return PySequence_List( trait->default_value ); case 4: return PyDict_Copy( trait->default_value ); case 5: return call_class( TraitListObject, trait, obj, name, trait->default_value ); case 6: return call_class( TraitDictObject, trait, obj, name, trait->default_value ); case 7: dv = trait->default_value; kw = PyTuple_GET_ITEM( dv, 2 ); if ( kw == Py_None ) kw = NULL; return PyObject_Call( PyTuple_GET_ITEM( dv, 0 ), PyTuple_GET_ITEM( dv, 1 ), kw ); case 8: if ( (tuple = PyTuple_New( 1 )) == NULL ) return NULL; PyTuple_SET_ITEM( tuple, 0, (PyObject *) obj ); Py_INCREF( obj ); result = PyObject_Call( trait->default_value, tuple, NULL ); Py_DECREF( tuple ); if ( (result != NULL) && (trait->validate != NULL) ) { value = trait->validate( trait, obj, name, result ); Py_DECREF( result ); return value; } break; case 9: return call_class( TraitSetObject, trait, obj, name, trait->default_value ); } return result; } /*----------------------------------------------------------------------------- | Returns the value assigned to a standard Python attribute: +----------------------------------------------------------------------------*/ static PyObject * getattr_python ( trait_object * trait, has_traits_object * obj, PyObject * name ) { return PyObject_GenericGetAttr( (PyObject *) obj, name ); } /*----------------------------------------------------------------------------- | Returns the value assigned to a generic Python attribute: +----------------------------------------------------------------------------*/ static PyObject * getattr_generic ( trait_object * trait, has_traits_object * obj, PyObject * name ) { return PyObject_GenericGetAttr( (PyObject *) obj, name ); } /*----------------------------------------------------------------------------- | Returns the value assigned to an event trait: +----------------------------------------------------------------------------*/ static PyObject * getattr_event ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyErr_Format( PyExc_AttributeError, "The %.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR " trait of a %.50s instance is an 'event', which is write only.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return NULL; } /*----------------------------------------------------------------------------- | Returns the value assigned to a standard trait: +----------------------------------------------------------------------------*/ static PyObject * getattr_trait ( trait_object * trait, has_traits_object * obj, PyObject * name ) { int rc; PyListObject * tnotifiers; PyListObject * onotifiers; PyObject * result; PyObject * nname; PyObject * dict = obj->obj_dict; if ( dict == NULL ) { dict = PyDict_New(); if ( dict == NULL ) return NULL; obj->obj_dict = dict; } if ( Py2to3_SimpleString_Check( name ) ) { if ( (result = default_value_for( trait, obj, name )) != NULL ) { if ( PyDict_SetItem( dict, name, result ) >= 0 ) { rc = 0; if ( (trait->post_setattr != NULL) && ((trait->flags & TRAIT_IS_MAPPED) == 0) ) rc = trait->post_setattr( trait, obj, name, result ); if (rc == 0) { tnotifiers = trait->notifiers; onotifiers = obj->notifiers; if ( has_notifiers( tnotifiers, onotifiers ) ) rc = call_notifiers( tnotifiers, onotifiers, obj, name, Uninitialized, result ); } if ( rc == 0 ) return result; } Py_DECREF( result ); } return NULL; } nname = Py2to3_NormaliseAttrName(name); if( nname == NULL ){ invalid_attribute_error( name ); return NULL; } if ( (result = default_value_for( trait, obj, nname )) != NULL ) { if ( PyDict_SetItem( dict, nname, result ) >= 0 ) { rc = 0; if ( (trait->post_setattr != NULL) && ((trait->flags & TRAIT_IS_MAPPED) == 0) ) rc = trait->post_setattr( trait, obj, nname, result ); if (rc == 0) { tnotifiers = trait->notifiers; onotifiers = obj->notifiers; if ( has_notifiers( tnotifiers, onotifiers ) ) rc = call_notifiers( tnotifiers, onotifiers, obj, nname, Uninitialized, result ); } if ( rc == 0 ){ Py2to3_FinishNormaliseAttrName(name,nname); return result; } } Py_DECREF( result ); } if ( PyErr_ExceptionMatches( PyExc_KeyError ) ) PyErr_SetObject( PyExc_AttributeError, nname ); Py2to3_FinishNormaliseAttrName(name,nname); Py_DECREF( name ); return NULL; } /*----------------------------------------------------------------------------- | Returns the value assigned to a delegated trait: +----------------------------------------------------------------------------*/ static PyObject * getattr_delegate ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyTypeObject * tp; PyObject * delegate_attr_name; PyObject * delegate; PyObject * result; PyObject * nname; PyObject * dict = obj->obj_dict; if ( (dict == NULL) || ((delegate = PyDict_GetItem( dict, trait->delegate_name )) == NULL) ){ // Handle the case when the delegate is not in the instance dictionary // (could be a method that returns the real delegate): delegate = has_traits_getattro( obj, trait->delegate_name ); if ( delegate == NULL ) return NULL; } else { Py_INCREF( delegate ); } nname = Py2to3_NormaliseAttrName(name); if( nname == NULL ){ invalid_attribute_error( name ); Py_DECREF( delegate ); return NULL; } delegate_attr_name = trait->delegate_attr_name( trait, obj, nname ); tp = Py_TYPE(delegate); if ( tp->tp_getattro != NULL ) { result = (*tp->tp_getattro)( delegate, delegate_attr_name ); goto done; } if ( tp->tp_getattr != NULL ) { PyObject *delegate_attr_name_c_str = Py2to3_AttrNameCStr( delegate_attr_name ); if(delegate_attr_name_c_str == NULL){ result = NULL; } else { result = (*tp->tp_getattr)( delegate, Py2to3_AttrName_AS_STRING( delegate_attr_name_c_str ) ); Py2to3_FinishAttrNameCStr(delegate_attr_name_c_str); goto done; } } PyErr_Format( DelegationError, "The '%.50s' object has no attribute '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " because its %.50s delegate has no attribute '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'.", Py_TYPE(obj)->tp_name, Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), tp->tp_name, Py2to3_PYERR_PREPARE_SIMPLE_STRING( delegate_attr_name ) ); result = NULL; done: Py_DECREF( delegate_attr_name ); Py2to3_FinishNormaliseAttrName(name,nname); Py_DECREF( delegate ); return result; } /*----------------------------------------------------------------------------- | Raises an exception when a disallowed trait is accessed: +----------------------------------------------------------------------------*/ static PyObject * getattr_disallow ( trait_object * trait, has_traits_object * obj, PyObject * name ) { if ( Py2to3_SimpleString_Check( name ) ) unknown_attribute_error( obj, name ); else invalid_attribute_error( name ); return NULL; } /*----------------------------------------------------------------------------- | Returns the value of a constant trait: +----------------------------------------------------------------------------*/ static PyObject * getattr_constant ( trait_object * trait, has_traits_object * obj, PyObject * name ) { Py_INCREF( trait->default_value ); return trait->default_value; } /*----------------------------------------------------------------------------- | Assigns a value to a specified property trait attribute: +----------------------------------------------------------------------------*/ static PyObject * getattr_property0 ( trait_object * trait, has_traits_object * obj, PyObject * name ) { return PyObject_Call( trait->delegate_name, empty_tuple, NULL ); } static PyObject * getattr_property1 ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyObject * result; PyObject * args = PyTuple_New( 1 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); Py_INCREF( obj ); result = PyObject_Call( trait->delegate_name, args, NULL ); Py_DECREF( args ); return result; } static PyObject * getattr_property2 ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyObject * result; PyObject * args = PyTuple_New( 2 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); Py_INCREF( obj ); PyTuple_SET_ITEM( args, 1, name ); Py_INCREF( name ); result = PyObject_Call( trait->delegate_name, args, NULL ); Py_DECREF( args ); return result; } static PyObject * getattr_property3 ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyObject * result; PyObject * args = PyTuple_New( 3 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); Py_INCREF( obj ); PyTuple_SET_ITEM( args, 1, name ); Py_INCREF( name ); PyTuple_SET_ITEM( args, 2, (PyObject *) trait ); Py_INCREF( trait ); result = PyObject_Call( trait->delegate_name, args, NULL ); Py_DECREF( args ); return result; } static trait_getattr getattr_property_handlers[] = { getattr_property0, getattr_property1, getattr_property2, getattr_property3 }; /*----------------------------------------------------------------------------- | Assigns a value to a specified standard Python attribute: +----------------------------------------------------------------------------*/ static int setattr_python ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject *nname; PyObject * dict = obj->obj_dict; if ( value != NULL ) { if ( dict == NULL ) { dict = PyDict_New(); if ( dict == NULL ) return -1; obj->obj_dict = dict; } nname = Py2to3_NormaliseAttrName( name ); if( nname == NULL ) return invalid_attribute_error( name ); if ( PyDict_SetItem( dict, nname, value ) >= 0 ){ Py2to3_FinishNormaliseAttrName(name,nname); return 0; } if ( PyErr_ExceptionMatches( PyExc_KeyError ) ) PyErr_SetObject( PyExc_AttributeError, nname ); Py2to3_FinishNormaliseAttrName(name,nname); return -1; } if ( dict != NULL ) { PyObject *nname = Py2to3_NormaliseAttrName( name ); if( nname == NULL ) return invalid_attribute_error( name ); if ( PyDict_DelItem( dict, nname ) >= 0 ){ Py2to3_FinishNormaliseAttrName(name,nname); return 0; } if ( PyErr_ExceptionMatches( PyExc_KeyError ) ) unknown_attribute_error( obj, nname ); Py2to3_FinishNormaliseAttrName(name,nname); return -1; } if ( Py2to3_SimpleString_Check( name ) ) { unknown_attribute_error( obj, name ); return -1; } return invalid_attribute_error( name ); } /*----------------------------------------------------------------------------- | Assigns a value to a specified generic Python attribute: +----------------------------------------------------------------------------*/ static int setattr_generic ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { return PyObject_GenericSetAttr( (PyObject *) obj, name, value ); } /*----------------------------------------------------------------------------- | Call all notifiers for a specified trait: +----------------------------------------------------------------------------*/ static int call_notifiers ( PyListObject * tnotifiers, PyListObject * onotifiers, has_traits_object * obj, PyObject * name, PyObject * old_value, PyObject * new_value ) { int i, n, new_value_has_traits; PyObject * result, * item, * temp; int rc = 0; PyObject * arg_temp = Py_None; PyObject * user_args = NULL; PyObject * args = PyTuple_New( 4 ); if ( args == NULL ) return -1; new_value_has_traits = PyHasTraits_Check( new_value ); PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, name ); PyTuple_SET_ITEM( args, 2, old_value ); PyTuple_SET_ITEM( args, 3, new_value ); Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( old_value ); Py_INCREF( new_value ); // Do nothing if the user has explicitly requested no traits notifications // to be sent. if ( (obj->flags & HASTRAITS_NO_NOTIFY) != 0 ) goto exit2; if ( _trait_notification_handler != NULL ) { user_args = PyTuple_New( 2 ); if ( user_args == NULL ) { Py_DECREF( args ); return -1; } PyTuple_SET_ITEM( user_args, 0, arg_temp ); PyTuple_SET_ITEM( user_args, 1, args ); Py_INCREF( arg_temp ); Py_INCREF( args ); } if ( tnotifiers != NULL ) { n = PyList_GET_SIZE( tnotifiers ); temp = NULL; if ( n > 1 ) { temp = PyList_New( n ); if ( temp == NULL ) { rc = -1; goto exit2; } for ( i = 0; i < n; i++ ) { item = PyList_GET_ITEM( tnotifiers, i ); PyList_SET_ITEM( temp, i, item ); Py_INCREF( item ); } tnotifiers = (PyListObject *) temp; } for ( i = 0; i < n; i++ ) { if ( new_value_has_traits && (((has_traits_object *) new_value)->flags & HASTRAITS_VETO_NOTIFY) ) { goto exit; } if ( (_trait_notification_handler != NULL) && (user_args != NULL) ){ Py_DECREF( arg_temp ); arg_temp = PyList_GET_ITEM( tnotifiers, i ); Py_INCREF( arg_temp ); PyTuple_SET_ITEM( user_args, 0, arg_temp ); result = PyObject_Call( _trait_notification_handler, user_args, NULL ); } else { result = PyObject_Call( PyList_GET_ITEM( tnotifiers, i ), args, NULL ); } if ( result == NULL ) { rc = -1; goto exit; } Py_DECREF( result ); } Py_XDECREF( temp ); } temp = NULL; if ( onotifiers != NULL ) { n = PyList_GET_SIZE( onotifiers ); if ( n > 1 ) { temp = PyList_New( n ); if ( temp == NULL ) { rc = -1; goto exit2; } for ( i = 0; i < n; i++ ) { item = PyList_GET_ITEM( onotifiers, i ); PyList_SET_ITEM( temp, i, item ); Py_INCREF( item ); } onotifiers = (PyListObject *) temp; } for ( i = 0; i < n; i++ ) { if ( new_value_has_traits && (((has_traits_object *) new_value)->flags & HASTRAITS_VETO_NOTIFY) ) { break; } if ( (_trait_notification_handler != NULL) && (user_args != NULL) ){ Py_DECREF( arg_temp ); arg_temp = PyList_GET_ITEM( onotifiers, i ); Py_INCREF( arg_temp ); PyTuple_SET_ITEM( user_args, 0, arg_temp ); result = PyObject_Call( _trait_notification_handler, user_args, NULL ); } else { result = PyObject_Call( PyList_GET_ITEM( onotifiers, i ), args, NULL ); } if ( result == NULL ) { rc = -1; goto exit; } Py_DECREF( result ); } } exit: Py_XDECREF( temp ); exit2: Py_XDECREF( user_args ); Py_DECREF( args ); return rc; } /*----------------------------------------------------------------------------- | Assigns a value to a specified event trait attribute: +----------------------------------------------------------------------------*/ static int setattr_event ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { int rc = 0; PyListObject * tnotifiers; PyListObject * onotifiers; if ( value != NULL ) { if ( traitd->validate != NULL ) { value = traitd->validate( traitd, obj, name, value ); if ( value == NULL ) return -1; } else { Py_INCREF( value ); } tnotifiers = traito->notifiers; onotifiers = obj->notifiers; if ( has_notifiers( tnotifiers, onotifiers ) ) rc = call_notifiers( tnotifiers, onotifiers, obj, name, Undefined, value ); Py_DECREF( value ); } return rc; } /*----------------------------------------------------------------------------- | Assigns a value to a specified normal trait attribute: +----------------------------------------------------------------------------*/ static int setattr_trait ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { int rc; int changed; int do_notifiers; trait_post_setattr post_setattr; PyListObject * tnotifiers = NULL; PyListObject * onotifiers = NULL; PyObject * old_value = NULL; PyObject * original_value; PyObject * new_value; PyObject *nname; PyObject * dict = obj->obj_dict; changed = (traitd->flags & TRAIT_NO_VALUE_TEST); if ( value == NULL ) { if ( dict == NULL ) return 0; nname = Py2to3_NormaliseAttrName(name); if( nname == NULL ) return invalid_attribute_error( name ); old_value = PyDict_GetItem( dict, nname ); if ( old_value == NULL ) { Py2to3_FinishNormaliseAttrName( name, nname ); return 0; } Py_INCREF( old_value ); if ( PyDict_DelItem( dict, nname ) < 0 ) { Py_DECREF( old_value ); Py2to3_FinishNormaliseAttrName( name, nname ); return -1; } rc = 0; if ( (obj->flags & HASTRAITS_NO_NOTIFY) == 0 ) { tnotifiers = traito->notifiers; onotifiers = obj->notifiers; if ( (tnotifiers != NULL) || (onotifiers != NULL) ) { value = traito->getattr( traito, obj, nname ); if ( value == NULL ) { Py_DECREF( old_value ); Py2to3_FinishNormaliseAttrName( name, nname ); return -1; } if ( !changed ) { changed = (old_value != value ); if ( changed && ((traitd->flags & TRAIT_OBJECT_IDENTITY) == 0) ) { changed = PyObject_RichCompareBool( old_value, value, Py_NE ); if ( changed == -1 ) { PyErr_Clear(); } } } if ( changed ) { if ( traitd->post_setattr != NULL ) rc = traitd->post_setattr( traitd, obj, nname, value ); if ( (rc == 0) && has_notifiers( tnotifiers, onotifiers ) ) rc = call_notifiers( tnotifiers, onotifiers, obj, nname, old_value, value ); } Py_DECREF( value ); } } Py_DECREF( old_value ); Py2to3_FinishNormaliseAttrName( name, nname ); return rc; } original_value = value; // If the object's value is Undefined, then do not call the validate // method (as the object's value has not yet been set). if ( ( traitd->validate != NULL ) && ( value != Undefined ) ) { value = traitd->validate( traitd, obj, name, value ); if ( value == NULL ) { return -1; } } else { Py_INCREF( value ); } if ( dict == NULL ) { obj->obj_dict = dict = PyDict_New(); if ( dict == NULL ) { Py_DECREF( value ); return -1; } } nname = Py2to3_NormaliseAttrName(name); if( nname == NULL ){ Py_DECREF( value ); return invalid_attribute_error( name ); } new_value = (traitd->flags & TRAIT_SETATTR_ORIGINAL_VALUE)? original_value: value; old_value = NULL; tnotifiers = traito->notifiers; onotifiers = obj->notifiers; do_notifiers = has_notifiers( tnotifiers, onotifiers ); post_setattr = traitd->post_setattr; if ( (post_setattr != NULL) || do_notifiers ) { old_value = PyDict_GetItem( dict, nname ); if ( old_value == NULL ) { if ( traitd != traito ) { old_value = traito->getattr( traito, obj, nname ); } else { old_value = default_value_for( traitd, obj, nname ); } if ( old_value == NULL ) { Py2to3_FinishNormaliseAttrName( name, nname ); Py_DECREF( value ); return -1; } } else { Py_INCREF( old_value ); } if ( !changed ) { changed = (old_value != value); if ( changed && ((traitd->flags & TRAIT_OBJECT_IDENTITY) == 0) ) { changed = PyObject_RichCompareBool( old_value, value, Py_NE ); if ( changed == -1 ) { PyErr_Clear(); } } } } if ( PyDict_SetItem( dict, nname, new_value ) < 0 ) { if ( PyErr_ExceptionMatches( PyExc_KeyError ) ) PyErr_SetObject( PyExc_AttributeError, nname ); Py_XDECREF( old_value ); Py_DECREF( name ); Py2to3_FinishNormaliseAttrName( name, nname ); Py_DECREF( value ); return -1; } rc = 0; if ( changed ) { if ( post_setattr != NULL ) rc = post_setattr( traitd, obj, nname, (traitd->flags & TRAIT_POST_SETATTR_ORIGINAL_VALUE)? original_value: value ); if ( (rc == 0) && do_notifiers ) rc = call_notifiers( tnotifiers, onotifiers, obj, nname, old_value, new_value ); } Py_XDECREF( old_value ); Py2to3_FinishNormaliseAttrName( name, nname ); Py_DECREF( value ); return rc; } /*----------------------------------------------------------------------------- | Assigns a value to a specified delegate trait attribute: +----------------------------------------------------------------------------*/ static int setattr_delegate ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * dict; PyObject * daname; PyObject * daname2; PyObject * temp; has_traits_object * delegate; has_traits_object * temp_delegate; int i, result; /* Follow the delegation chain until we find a non-delegated trait: */ daname = name; Py_INCREF( daname ); delegate = obj; for ( i = 0; ; ) { dict = delegate->obj_dict; if ( (dict != NULL) && ((temp_delegate = (has_traits_object *) PyDict_GetItem( dict, traitd->delegate_name )) != NULL) ) { delegate = temp_delegate; } else { // Handle the case when the delegate is not in the instance // dictionary (could be a method that returns the real delegate): delegate = (has_traits_object *) has_traits_getattro( delegate, traitd->delegate_name ); if ( delegate == NULL ) { Py_DECREF( daname ); return -1; } Py_DECREF( delegate ); } // Verify that 'delegate' is of type 'CHasTraits': if ( !PyHasTraits_Check( delegate ) ) { Py_DECREF( daname ); return bad_delegate_error2( obj, name ); } daname2 = traitd->delegate_attr_name( traitd, obj, daname ); Py_DECREF( daname ); daname = daname2; if ( ((delegate->itrait_dict == NULL) || ((traitd = (trait_object *) dict_getitem( delegate->itrait_dict, daname )) == NULL)) && ((traitd = (trait_object *) dict_getitem( delegate->ctrait_dict, daname )) == NULL) && ((traitd = get_prefix_trait( delegate, daname, 1 )) == NULL) ) { Py_DECREF( daname ); return bad_delegate_error( obj, name ); } if ( Py_TYPE(traitd) != ctrait_type ) { Py_DECREF( daname ); return fatal_trait_error(); } if ( traitd->delegate_attr_name == NULL ) { if ( traito->flags & TRAIT_MODIFY_DELEGATE ) { result = traitd->setattr( traitd, traitd, delegate, daname, value ); } else { result = traitd->setattr( traito, traitd, obj, name, value ); if ( result >= 0 ) { temp = PyObject_CallMethod( (PyObject *) obj, "_remove_trait_delegate_listener", "(Oi)", name, value != NULL ); if ( temp == NULL ) { result = -1; } else { Py_DECREF( temp ); } } } Py_DECREF( daname ); return result; } if ( ++i >= 100 ) return delegation_recursion_error( obj, name ); } } /*----------------------------------------------------------------------------- | Assigns a value to a specified property trait attribute: +----------------------------------------------------------------------------*/ static int setattr_property0 ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; if ( value == NULL ) return set_delete_property_error( obj, name ); result = PyObject_Call( traitd->delegate_prefix, empty_tuple, NULL ); if ( result == NULL ) return -1; Py_DECREF( result ); return 0; } static int setattr_property1 ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args; if ( value == NULL ) return set_delete_property_error( obj, name ); args = PyTuple_New( 1 ); if ( args == NULL ) return -1; PyTuple_SET_ITEM( args, 0, value ); Py_INCREF( value ); result = PyObject_Call( traitd->delegate_prefix, args, NULL ); Py_DECREF( args ); if ( result == NULL ) return -1; Py_DECREF( result ); return 0; } static int setattr_property2 ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args; if ( value == NULL ) return set_delete_property_error( obj, name ); args = PyTuple_New( 2 ); if ( args == NULL ) return -1; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, value ); Py_INCREF( obj ); Py_INCREF( value ); result = PyObject_Call( traitd->delegate_prefix, args, NULL ); Py_DECREF( args ); if ( result == NULL ) return -1; Py_DECREF( result ); return 0; } static int setattr_property3 ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args; if ( value == NULL ) return set_delete_property_error( obj, name ); args = PyTuple_New( 3 ); if ( args == NULL ) return -1; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, name ); PyTuple_SET_ITEM( args, 2, value ); Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( value ); result = PyObject_Call( traitd->delegate_prefix, args, NULL ); Py_DECREF( args ); if ( result == NULL ) return -1; Py_DECREF( result ); return 0; } /*----------------------------------------------------------------------------- | Validates then assigns a value to a specified property trait attribute: +----------------------------------------------------------------------------*/ static int setattr_validate_property ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { int result; PyObject * validated; if ( value == NULL ) return set_delete_property_error( obj, name ); validated = traitd->validate( traitd, obj, name, value ); if ( validated == NULL ) return -1; result = ((trait_setattr) traitd->post_setattr)( traito, traitd, obj, name, validated ); Py_DECREF( validated ); return result; } static PyObject * setattr_validate0 ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { return PyObject_Call( trait->py_validate, empty_tuple, NULL ); } static PyObject * setattr_validate1 ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * validated; PyObject * args = PyTuple_New( 1 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, value ); Py_INCREF( value ); validated = PyObject_Call( trait->py_validate, args, NULL ); Py_DECREF( args ); return validated; } static PyObject * setattr_validate2 ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * validated; PyObject * args = PyTuple_New( 2 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, value ); Py_INCREF( obj ); Py_INCREF( value ); validated = PyObject_Call( trait->py_validate, args, NULL ); Py_DECREF( args ); return validated; } static PyObject * setattr_validate3 ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * validated; PyObject * args = PyTuple_New( 3 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, name ); PyTuple_SET_ITEM( args, 2, value ); Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( value ); validated = PyObject_Call( trait->py_validate, args, NULL ); Py_DECREF( args ); return validated; } trait_validate setattr_validate_handlers[] = { setattr_validate0, setattr_validate1, setattr_validate2, setattr_validate3 }; /*----------------------------------------------------------------------------- | Raises an exception when attempting to assign to a disallowed trait: +----------------------------------------------------------------------------*/ static int setattr_disallow ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { return set_disallow_error( obj, name ); } /*----------------------------------------------------------------------------- | Assigns a value to a specified read-only trait attribute: +----------------------------------------------------------------------------*/ static int setattr_readonly ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * dict; PyObject * result; PyObject * nname; int rc; if ( value == NULL ) return delete_readonly_error( obj, name ); if ( traitd->default_value != Undefined ) return set_readonly_error( obj, name ); dict = obj->obj_dict; if ( dict == NULL ) return setattr_python( traito, traitd, obj, name, value ); nname = Py2to3_NormaliseAttrName(name); if( nname == NULL ){ return invalid_attribute_error( name ); } result = PyDict_GetItem( dict, nname ); if ( (result == NULL) || (result == Undefined) ) rc = setattr_python( traito, traitd, obj, nname, value ); else rc = set_readonly_error( obj, nname ); Py2to3_FinishNormaliseAttrName(name,nname); return rc; } /*----------------------------------------------------------------------------- | Generates exception on attempting to assign to a constant trait: +----------------------------------------------------------------------------*/ static int setattr_constant ( trait_object * traito, trait_object * traitd, has_traits_object * obj, PyObject * name, PyObject * value ) { if ( Py2to3_SimpleString_Check( name ) ) { PyErr_Format( TraitError, "Cannot modify the constant '%.400" Py2to3_PYERR_SIMPLE_STRING_FMTCHR "'" " attribute of a '%.50s' object.", Py2to3_PYERR_PREPARE_SIMPLE_STRING( name ), Py_TYPE(obj)->tp_name ); return -1; } return invalid_attribute_error( name ); } /*----------------------------------------------------------------------------- | Initializes a CTrait instance: +----------------------------------------------------------------------------*/ static trait_getattr getattr_handlers[] = { getattr_trait, getattr_python, getattr_event, getattr_delegate, getattr_event, getattr_disallow, getattr_trait, getattr_constant, getattr_generic, /* The following entries are used by the __getstate__ method: */ getattr_property0, getattr_property1, getattr_property2, getattr_property3, /* End of __getstate__ method entries */ NULL }; static trait_setattr setattr_handlers[] = { setattr_trait, setattr_python, setattr_event, setattr_delegate, setattr_event, setattr_disallow, setattr_readonly, setattr_constant, setattr_generic, /* The following entries are used by the __getstate__ method: */ setattr_property0, setattr_property1, setattr_property2, setattr_property3, /* End of __setstate__ method entries */ NULL }; static int trait_init ( trait_object * trait, PyObject * args, PyObject * kwds ) { int kind; if ( !PyArg_ParseTuple( args, "i", &kind ) ) return -1; if ( (kind >= 0) && (kind <= 8) ) { trait->getattr = getattr_handlers[ kind ]; trait->setattr = setattr_handlers[ kind ]; return 0; } return bad_trait_error(); } /*----------------------------------------------------------------------------- | Object clearing method: +----------------------------------------------------------------------------*/ static int trait_clear ( trait_object * trait ) { Py_CLEAR( trait->default_value ); Py_CLEAR( trait->py_validate ); Py_CLEAR( trait->py_post_setattr ); Py_CLEAR( trait->delegate_name ); Py_CLEAR( trait->delegate_prefix ); Py_CLEAR( trait->notifiers ); Py_CLEAR( trait->handler ); Py_CLEAR( trait->obj_dict ); return 0; } /*----------------------------------------------------------------------------- | Deallocates an unused 'CTrait' instance: +----------------------------------------------------------------------------*/ static void trait_dealloc ( trait_object * trait ) { PyObject_GC_UnTrack(trait); Py_TRASHCAN_SAFE_BEGIN(trait); trait_clear( trait ); Py_TYPE(trait)->tp_free( (PyObject *) trait ); Py_TRASHCAN_SAFE_END(trait); } /*----------------------------------------------------------------------------- | Garbage collector traversal method: +----------------------------------------------------------------------------*/ static int trait_traverse ( trait_object * trait, visitproc visit, void * arg ) { Py_VISIT( trait->default_value ); Py_VISIT( trait->py_validate ); Py_VISIT( trait->py_post_setattr ); Py_VISIT( trait->delegate_name ); Py_VISIT( trait->delegate_prefix ); Py_VISIT( (PyObject *) trait->notifiers ); Py_VISIT( trait->handler ); Py_VISIT( trait->obj_dict ); return 0; } /*----------------------------------------------------------------------------- | Casts a 'CTrait' which attempts to validate the argument passed as being a | valid value for the trait: +----------------------------------------------------------------------------*/ static PyObject * _trait_cast ( trait_object * trait, PyObject * args ) { PyObject * obj; PyObject * name; PyObject * value; PyObject * result; PyObject * info; switch ( PyTuple_GET_SIZE( args ) ) { case 1: obj = name = Py_None; value = PyTuple_GET_ITEM( args, 0 ); break; case 2: name = Py_None; obj = PyTuple_GET_ITEM( args, 0 ); value = PyTuple_GET_ITEM( args, 1 ); break; case 3: obj = PyTuple_GET_ITEM( args, 0 ); name = PyTuple_GET_ITEM( args, 1 ); value = PyTuple_GET_ITEM( args, 2 ); break; default: PyErr_Format( PyExc_TypeError, #if PY_VERSION_HEX >= 0x02050000 "Trait cast takes 1, 2 or 3 arguments (%zd given).", #else "Trait cast takes 1, 2 or 3 arguments (%u given).", #endif PyTuple_GET_SIZE( args ) ); return NULL; } if ( trait->validate == NULL ) { Py_INCREF( value ); return value; } result = trait->validate( trait, (has_traits_object *) obj, name, value ); if ( result == NULL ) { PyErr_Clear(); info = PyObject_CallMethod( trait->handler, "info", NULL ); if ( (info != NULL) && Py2to3_SimpleString_Check( info ) ) PyErr_Format( PyExc_ValueError, "Invalid value for trait, the value should be %" Py2to3_PYERR_SIMPLE_STRING_FMTCHR ".", Py2to3_PYERR_PREPARE_SIMPLE_STRING( info ) ); else PyErr_Format( PyExc_ValueError, "Invalid value for trait." ); Py_XDECREF( info ); } return result; } /*----------------------------------------------------------------------------- | Handles the 'getattr' operation on a 'CHasTraits' instance: +----------------------------------------------------------------------------*/ static PyObject * trait_getattro ( trait_object * obj, PyObject * name ) { PyObject * value = PyObject_GenericGetAttr( (PyObject *) obj, name ); if ( value != NULL ) return value; PyErr_Clear(); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the value of the 'default_value' field of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_default_value ( trait_object * trait, PyObject * args ) { int value_type; PyObject * value; if ( PyArg_ParseTuple( args, "" ) ) { if ( trait->default_value == NULL ) return Py_BuildValue( "iO", 0, Py_None ); return Py_BuildValue( "iO", trait->default_value_type, trait->default_value ); } if ( !PyArg_ParseTuple( args, "iO", &value_type, &value ) ) return NULL; PyErr_Clear(); if ( (value_type < 0) || (value_type > 9) ) { PyErr_Format( PyExc_ValueError, "The default value type must be 0..9, but %d was specified.", value_type ); return NULL; } Py_INCREF( value ); Py_XDECREF( trait->default_value ); trait->default_value_type = value_type; trait->default_value = value; Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Gets the default value of a CTrait instance for a specified object and trait | name: +----------------------------------------------------------------------------*/ static PyObject * _trait_default_value_for ( trait_object * trait, PyObject * args ) { PyObject * object; PyObject * name; if ( !PyArg_ParseTuple( args, "OO", &object, &name ) ) return NULL; return default_value_for( trait, (has_traits_object *) object, name ); } /*----------------------------------------------------------------------------- | Calls a Python-based trait validator: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_python ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args = PyTuple_New( 3 ); if ( args == NULL ) return NULL; Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( value ); PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, name ); PyTuple_SET_ITEM( args, 2, value ); result = PyObject_Call( trait->py_validate, args, NULL ); Py_DECREF( args ); return result; } /*----------------------------------------------------------------------------- | Calls the specified validator function: +----------------------------------------------------------------------------*/ static PyObject * call_validator ( PyObject * validator, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args = PyTuple_New( 3 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, name ); PyTuple_SET_ITEM( args, 2, value ); Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( value ); result = PyObject_Call( validator, args, NULL ); Py_DECREF( args ); return result; } /*----------------------------------------------------------------------------- | Calls the specified type convertor: +----------------------------------------------------------------------------*/ static PyObject * type_converter ( PyObject * type, PyObject * value ) { PyObject * result; PyObject * args = PyTuple_New( 1 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 0, value ); Py_INCREF( value ); result = PyObject_Call( type, args, NULL ); Py_DECREF( args ); return result; } /*----------------------------------------------------------------------------- | Verifies a Python value is of a specified type (or None): +----------------------------------------------------------------------------*/ static PyObject * validate_trait_type ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * type_info = trait->py_validate; int kind = PyTuple_GET_SIZE( type_info ); if ( ((kind == 3) && (value == Py_None)) || PyObject_TypeCheck( value, (PyTypeObject *) PyTuple_GET_ITEM( type_info, kind - 1 ) ) ) { Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is an instance of a specified type (or None): +----------------------------------------------------------------------------*/ static PyObject * validate_trait_instance ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * type_info = trait->py_validate; int kind = PyTuple_GET_SIZE( type_info ); if ( ((kind == 3) && (value == Py_None)) || (PyObject_IsInstance( value, PyTuple_GET_ITEM( type_info, kind - 1 ) ) > 0) ) { Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is of a the same type as the object being assigned | to (or None): +----------------------------------------------------------------------------*/ static PyObject * validate_trait_self_type ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { if ( ((PyTuple_GET_SIZE( trait->py_validate ) == 2) && (value == Py_None)) || PyObject_TypeCheck( value, Py_TYPE(obj) ) ) { Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is an int within a specified range: +----------------------------------------------------------------------------*/ #if PY_MAJOR_VERSION < 3 static PyObject * validate_trait_int ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { register PyObject * low; register PyObject * high; long exclude_mask; long int_value; PyObject * type_info = trait->py_validate; if ( PyInt_Check( value ) ) { int_value = PyInt_AS_LONG( value ); low = PyTuple_GET_ITEM( type_info, 1 ); high = PyTuple_GET_ITEM( type_info, 2 ); exclude_mask = PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 3 ) ); if ( low != Py_None ) { if ( (exclude_mask & 1) != 0 ) { if ( int_value <= PyInt_AS_LONG( low ) ) goto error; } else { if ( int_value < PyInt_AS_LONG( low ) ) goto error; } } if ( high != Py_None ) { if ( (exclude_mask & 2) != 0 ) { if ( int_value >= PyInt_AS_LONG( high ) ) goto error; } else { if ( int_value > PyInt_AS_LONG( high ) ) goto error; } } Py_INCREF( value ); return value; } error: return raise_trait_error( trait, obj, name, value ); } #endif // #if PY_MAJOR_VERSION < 3 /*----------------------------------------------------------------------------- | Verifies a Python value is a Python integer (an int or long) +----------------------------------------------------------------------------*/ static PyObject * validate_trait_integer ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject *int_value, *result; /* Fast paths for the most common cases. */ #if PY_MAJOR_VERSION < 3 if (PyInt_CheckExact(value)) { Py_INCREF(value); return value; } else if (PyLong_CheckExact(value)) { long x; x = PyLong_AsLong(value); if (x == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Clear(); Py_INCREF(value); return value; } return NULL; } else { return PyInt_FromLong(x); } } #else if (PyLong_CheckExact(value)) { Py_INCREF(value); return value; } #endif // #if PY_MAJOR_VERSION < 3 /* General case. The effect is supposed to be that of int(operator.index(value)). The extra call to 'int' is necessary because in Python 2, operator.index (somewhat controversially) does *not* always return something of type int or long, but can return instances of subclasses of int or long. */ int_value = PyNumber_Index(value); if (int_value == NULL) { /* Translate a TypeError to a TraitError, but pass on other exceptions. */ if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Clear(); goto error; } return NULL; } #if PY_MAJOR_VERSION < 3 result = PyNumber_Int(int_value); #else result = PyNumber_Long(int_value); #endif // #if PY_MAJOR_VERSION < 3 Py_DECREF(int_value); return result; error: return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is a float within a specified range: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_float ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { register PyObject * low; register PyObject * high; long exclude_mask; double float_value; PyObject * type_info = trait->py_validate; if ( !PyFloat_Check( value ) ) { float_value = Py2to3_PyNum_AsDouble( value ); if( float_value==-1 && PyErr_Occurred() ) goto error; value = PyFloat_FromDouble( float_value ); if ( value == NULL ) goto error; Py_INCREF( value ); } else { float_value = PyFloat_AS_DOUBLE( value ); } low = PyTuple_GET_ITEM( type_info, 1 ); high = PyTuple_GET_ITEM( type_info, 2 ); #if PY_MAJOR_VERSION < 3 exclude_mask = PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 3 ) ); #else exclude_mask = PyLong_AsLong( PyTuple_GET_ITEM( type_info, 3 ) ); if( exclude_mask==-1 && PyErr_Occurred()){ goto error; } #endif // #if PY_MAJOR_VERSION < 3 if ( low != Py_None ) { if ( (exclude_mask & 1) != 0 ) { if ( float_value <= PyFloat_AS_DOUBLE( low ) ) goto error; } else { if ( float_value < PyFloat_AS_DOUBLE( low ) ) goto error; } } if ( high != Py_None ) { if ( (exclude_mask & 2) != 0 ) { if ( float_value >= PyFloat_AS_DOUBLE( high ) ) goto error; } else { if ( float_value > PyFloat_AS_DOUBLE( high ) ) goto error; } } Py_INCREF( value ); return value; error: return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is in a specified enumeration: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_enum ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * type_info = trait->py_validate; if ( PySequence_Contains( PyTuple_GET_ITEM( type_info, 1 ), value ) > 0 ) { Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is in a specified map (i.e. dictionary): +----------------------------------------------------------------------------*/ static PyObject * validate_trait_map ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * type_info = trait->py_validate; if ( PyDict_GetItem( PyTuple_GET_ITEM( type_info, 1 ), value ) != NULL ) { Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is in a specified prefix map (i.e. dictionary): +----------------------------------------------------------------------------*/ static PyObject * validate_trait_prefix_map ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * type_info = trait->py_validate; PyObject * mapped_value = PyDict_GetItem( PyTuple_GET_ITEM( type_info, 1 ), value ); if ( mapped_value != NULL ) { Py_INCREF( mapped_value ); return mapped_value; } return call_validator( PyTuple_GET_ITEM( trait->py_validate, 2 ), obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is a tuple of a specified type and content: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_tuple_check ( PyObject * traits, has_traits_object * obj, PyObject * name, PyObject * value ) { trait_object * itrait; PyObject * bitem, * aitem, * tuple; int i, j, n; if ( PyTuple_Check( value ) ) { n = PyTuple_GET_SIZE( traits ); if ( n == PyTuple_GET_SIZE( value ) ) { tuple = NULL; for ( i = 0; i < n; i++ ) { bitem = PyTuple_GET_ITEM( value, i ); itrait = (trait_object *) PyTuple_GET_ITEM( traits, i ); if ( itrait->validate == NULL ) { aitem = bitem; Py_INCREF( aitem ); } else aitem = itrait->validate( itrait, obj, name, bitem ); if ( aitem == NULL ) { PyErr_Clear(); Py_XDECREF( tuple ); return NULL; } if ( tuple != NULL ) PyTuple_SET_ITEM( tuple, i, aitem ); else if ( aitem != bitem ) { tuple = PyTuple_New( n ); if ( tuple == NULL ) return NULL; for ( j = 0; j < i; j++ ) { bitem = PyTuple_GET_ITEM( value, j ); Py_INCREF( bitem ); PyTuple_SET_ITEM( tuple, j, bitem ); } PyTuple_SET_ITEM( tuple, i, aitem ); } else Py_DECREF( aitem ); } if ( tuple != NULL ) return tuple; Py_INCREF( value ); return value; } } return NULL; } static PyObject * validate_trait_tuple ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result = validate_trait_tuple_check( PyTuple_GET_ITEM( trait->py_validate, 1 ), obj, name, value ); if ( result != NULL ) return result; return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is of a specified (possibly coercable) type: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_coerce_type ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { int i, n; PyObject * type2; PyObject * type_info = trait->py_validate; PyObject * type = PyTuple_GET_ITEM( type_info, 1 ); if ( PyObject_TypeCheck( value, (PyTypeObject *) type ) ) { Py_INCREF( value ); return value; } n = PyTuple_GET_SIZE( type_info ); for ( i = 2; i < n; i++ ) { type2 = PyTuple_GET_ITEM( type_info, i ); if ( type2 == Py_None ) break; if ( PyObject_TypeCheck( value, (PyTypeObject *) type2 ) ) { Py_INCREF( value ); return value; } } for ( i++; i < n; i++ ) { type2 = PyTuple_GET_ITEM( type_info, i ); if ( PyObject_TypeCheck( value, (PyTypeObject *) type2 ) ) return type_converter( type, value ); } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value is of a specified (possibly castable) type: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_cast_type ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * type_info = trait->py_validate; PyObject * type = PyTuple_GET_ITEM( type_info, 1 ); if ( PyObject_TypeCheck( value, (PyTypeObject *) type ) ) { Py_INCREF( value ); return value; } if ( (result = type_converter( type, value )) != NULL ) return result; return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value satisifies a specified function validator: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_function ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; result = call_validator( PyTuple_GET_ITEM( trait->py_validate, 1 ), obj, name, value ); if ( result != NULL ) return result; return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Attempts to 'adapt' an object to a specified interface: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_adapt ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args; PyObject * type; PyObject * type_info = trait->py_validate; long mode, rc; if ( value == Py_None ) { #if PY_MAJOR_VERSION < 3 if ( PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 3 ) ) ) { #else mode = PyLong_AsLong( PyTuple_GET_ITEM( type_info, 3 ) ); if( mode==-1 && PyErr_Occurred()) return NULL; if ( mode ) { #endif // #if PY_MAJOR_VERSION < 3 Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } type = PyTuple_GET_ITEM( type_info, 1 ); #if PY_MAJOR_VERSION < 3 mode = PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 2 ) ); #else mode = PyLong_AsLong( PyTuple_GET_ITEM( type_info, 2 ) ); if( mode==-1 && PyErr_Occurred()) return NULL; #endif // #if PY_MAJOR_VERSION < 3 if ( mode == 2 ) { args = PyTuple_New( 3 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 2, Py_None ); Py_INCREF( Py_None ); } else { args = PyTuple_New( 2 ); if ( args == NULL ) return NULL; } PyTuple_SET_ITEM( args, 0, value ); PyTuple_SET_ITEM( args, 1, type ); Py_INCREF( value ); Py_INCREF( type ); result = PyObject_Call( adapt, args, NULL ); if ( result != NULL ) { if ( result != Py_None ) { if ( (mode > 0) || (result == value) ) { Py_DECREF( args ); return result; } Py_DECREF( result ); goto check_implements; } Py_DECREF( result ); result = PyObject_Call( validate_implements, args, NULL ); #if PY_MAJOR_VERSION < 3 rc = PyInt_AS_LONG( result ); #else rc = PyLong_AsLong( result ); #endif Py_DECREF( args ); Py_DECREF( result ); #if PY_MAJOR_VERSION >= 3 if( rc==-1 && PyErr_Occurred()){ return NULL; } #endif if ( rc ) { Py_INCREF( value ); return value; } result = default_value_for( trait, obj, name ); if ( result != NULL ) return result; PyErr_Clear(); return raise_trait_error( trait, obj, name, value ); } PyErr_Clear(); check_implements: result = PyObject_Call( validate_implements, args, NULL ); #if PY_MAJOR_VERSION < 3 rc = PyInt_AS_LONG( result ); #else rc = PyLong_AsLong( result ); #endif Py_DECREF( args ); Py_DECREF( result ); #if PY_MAJOR_VERSION >= 3 if( rc==-1 && PyErr_Occurred()){ return NULL; } #endif if ( rc ) { Py_INCREF( value ); return value; } return raise_trait_error( trait, obj, name, value ); } /*----------------------------------------------------------------------------- | Verifies a Python value satisifies a complex trait definition: +----------------------------------------------------------------------------*/ static PyObject * validate_trait_complex ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { int i, j, k, kind; long exclude_mask, mode, rc; double float_value; PyObject * low, * high, * result, * type_info, * type, * type2, * args; PyObject * int_value; PyObject * list_type_info = PyTuple_GET_ITEM( trait->py_validate, 1 ); int n = PyTuple_GET_SIZE( list_type_info ); for ( i = 0; i < n; i++ ) { type_info = PyTuple_GET_ITEM( list_type_info, i ); switch ( Py2to3_PyNum_AsLong( PyTuple_GET_ITEM( type_info, 0 ) ) ) { case 0: /* Type check: */ kind = PyTuple_GET_SIZE( type_info ); if ( ((kind == 3) && (value == Py_None)) || PyObject_TypeCheck( value, (PyTypeObject *) PyTuple_GET_ITEM( type_info, kind - 1 ) ) ) goto done; break; case 1: /* Instance check: */ kind = PyTuple_GET_SIZE( type_info ); if ( ((kind == 3) && (value == Py_None)) || (PyObject_IsInstance( value, PyTuple_GET_ITEM( type_info, kind - 1 ) ) > 0) ) goto done; break; case 2: /* Self type check: */ if ( ((PyTuple_GET_SIZE( type_info ) == 2) && (value == Py_None)) || PyObject_TypeCheck( value, Py_TYPE(obj) ) ) goto done; break; #if PY_MAJOR_VERSION < 3 case 3: /* Integer range check: */ if ( PyInt_Check( value ) ) { long int_value; int_value = PyInt_AS_LONG( value ); low = PyTuple_GET_ITEM( type_info, 1 ); high = PyTuple_GET_ITEM( type_info, 2 ); exclude_mask = PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 3 ) ); if ( low != Py_None ) { if ( (exclude_mask & 1) != 0 ) { if ( int_value <= PyInt_AS_LONG( low ) ) break; } else { if ( int_value < PyInt_AS_LONG( low ) ) break; } } if ( high != Py_None ) { if ( (exclude_mask & 2) != 0 ) { if ( int_value >= PyInt_AS_LONG( high ) ) break; } else { if ( int_value > PyInt_AS_LONG( high ) ) break; } } goto done; } break; #endif case 4: /* Floating point range check: */ if ( !PyFloat_Check( value ) ) { float_value = Py2to3_PyNum_AsDouble( value ); if( float_value==-1 && PyErr_Occurred() ){ PyErr_Clear(); break; } value = PyFloat_FromDouble( float_value ); if ( value == NULL ) { PyErr_Clear(); break; } } else { float_value = PyFloat_AS_DOUBLE( value ); Py_INCREF( value ); } low = PyTuple_GET_ITEM( type_info, 1 ); high = PyTuple_GET_ITEM( type_info, 2 ); #if PY_MAJOR_VERSION < 3 exclude_mask = PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 3 ) ); #else exclude_mask = PyLong_AsLong( PyTuple_GET_ITEM( type_info, 3 ) ); if( exclude_mask==-1 && PyErr_Occurred()){ PyErr_Clear(); break; } #endif // #if PY_MAJOR_VERSION < 3 if ( low != Py_None ) { if ( (exclude_mask & 1) != 0 ) { if ( float_value <= PyFloat_AS_DOUBLE( low ) ) break; } else { if ( float_value < PyFloat_AS_DOUBLE( low ) ) break; } } if ( high != Py_None ) { if ( (exclude_mask & 2) != 0 ) { if ( float_value >= PyFloat_AS_DOUBLE( high ) ) break; } else { if ( float_value > PyFloat_AS_DOUBLE( high ) ) break; } } goto done2; case 5: /* Enumerated item check: */ if ( PySequence_Contains( PyTuple_GET_ITEM( type_info, 1 ), value ) > 0 ) goto done; break; case 6: /* Mapped item check: */ if ( PyDict_GetItem( PyTuple_GET_ITEM( type_info, 1 ), value ) != NULL ) goto done; PyErr_Clear(); break; case 8: /* Perform 'slow' validate check: */ result = PyObject_CallMethod( PyTuple_GET_ITEM( type_info, 1 ), "slow_validate", "(OOO)", obj, name, value ); if ( result != NULL ) return result; PyErr_Clear(); break; case 9: /* Tuple item check: */ result = validate_trait_tuple_check( PyTuple_GET_ITEM( type_info, 1 ), obj, name, value ); if ( result != NULL ) return result; PyErr_Clear(); break; case 10: /* Prefix map item check: */ result = PyDict_GetItem( PyTuple_GET_ITEM( type_info, 1 ), value ); if ( result != NULL ) { Py_INCREF( result ); return result; } result = call_validator( PyTuple_GET_ITEM( type_info, 2 ), obj, name, value ); if ( result != NULL ) return result; PyErr_Clear(); break; case 11: /* Coercable type check: */ type = PyTuple_GET_ITEM( type_info, 1 ); if ( PyObject_TypeCheck( value, (PyTypeObject *) type ) ) goto done; k = PyTuple_GET_SIZE( type_info ); for ( j = 2; j < k; j++ ) { type2 = PyTuple_GET_ITEM( type_info, j ); if ( type2 == Py_None ) break; if ( PyObject_TypeCheck( value, (PyTypeObject *) type2 ) ) goto done; } for ( j++; j < k; j++ ) { type2 = PyTuple_GET_ITEM( type_info, j ); if ( PyObject_TypeCheck( value, (PyTypeObject *) type2 ) ) return type_converter( type, value ); } break; case 12: /* Castable type check */ type = PyTuple_GET_ITEM( type_info, 1 ); if ( PyObject_TypeCheck( value, (PyTypeObject *) type ) ) goto done; if ( (result = type_converter( type, value )) != NULL ) return result; PyErr_Clear(); break; case 13: /* Function validator check: */ result = call_validator( PyTuple_GET_ITEM( type_info, 1 ), obj, name, value ); if ( result != NULL ) return result; PyErr_Clear(); break; /* case 14: Python-based validator check: */ /* case 15..18: Property 'setattr' validate checks: */ case 19: /* PyProtocols 'adapt' check: */ if ( value == Py_None ) { #if PY_MAJOR_VERSION < 3 if ( PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 3 ) ) ) #else mode = PyLong_AsLong( PyTuple_GET_ITEM( type_info, 2 ) ); if( mode==-1 && PyErr_Occurred()) return NULL; if( mode ) #endif // #if PY_MAJOR_VERSION < 3 goto done; break; } type = PyTuple_GET_ITEM( type_info, 1 ); #if PY_MAJOR_VERSION < 3 mode = PyInt_AS_LONG( PyTuple_GET_ITEM( type_info, 2 ) ); #else mode = PyLong_AsLong( PyTuple_GET_ITEM( type_info, 2 ) ); if( mode==-1 && PyErr_Occurred()) return NULL; #endif // #if PY_MAJOR_VERSION < 3 if ( mode == 2 ) { args = PyTuple_New( 3 ); if ( args == NULL ) return NULL; PyTuple_SET_ITEM( args, 2, Py_None ); Py_INCREF( Py_None ); } else { args = PyTuple_New( 2 ); if ( args == NULL ) return NULL; } PyTuple_SET_ITEM( args, 0, value ); PyTuple_SET_ITEM( args, 1, type ); Py_INCREF( value ); Py_INCREF( type ); result = PyObject_Call( adapt, args, NULL ); if ( result != NULL ) { if ( result != Py_None ) { if ( (mode == 0) && (result != value) ) { Py_DECREF( result ); goto check_implements; } Py_DECREF( args ); return result; } Py_DECREF( result ); result = PyObject_Call( validate_implements, args, NULL ); #if PY_MAJOR_VERSION < 3 rc = PyInt_AS_LONG( result ); #else rc = PyLong_AsLong( result ); if( rc==-1 && PyErr_Occurred()){ PyErr_Clear(); Py_DECREF( args ); Py_DECREF( result ); break; } #endif // #if PY_MAJOR_VERSION < 3 Py_DECREF( args ); Py_DECREF( result ); if ( rc ) goto done; result = default_value_for( trait, obj, name ); if ( result != NULL ) return result; PyErr_Clear(); break; } PyErr_Clear(); check_implements: result = PyObject_Call( validate_implements, args, NULL ); #if PY_MAJOR_VERSION < 3 rc = PyInt_AS_LONG( result ); #else rc = PyLong_AsLong( result ); if( rc==-1 && PyErr_Occurred()){ PyErr_Clear(); Py_DECREF( args ); Py_DECREF( result ); break; } #endif // #if PY_MAJOR_VERSION < 3 Py_DECREF( args ); Py_DECREF( result ); if ( rc ) goto done; break; case 20: /* Integer check: */ /* Fast paths for the most common cases. */ #if PY_MAJOR_VERSION < 3 if (PyInt_CheckExact(value)) { Py_INCREF(value); return value; } else if (PyLong_CheckExact(value)) { long x; x = PyLong_AsLong(value); if (x == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Clear(); Py_INCREF(value); return value; } return NULL; } else { return PyInt_FromLong(x); } } #else if (PyLong_CheckExact(value)) { Py_INCREF(value); return value; } #endif // #if PY_MAJOR_VERSION < 3 /* General case. */ int_value = PyNumber_Index(value); if (int_value == NULL) { /* Translate a TypeError to a TraitError, but pass on other exceptions. */ if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Clear(); break; } return NULL; } #if PY_MAJOR_VERSION < 3 result = PyNumber_Int(int_value); #else result = PyNumber_Long(int_value); #endif // #if PY_MAJOR_VERSION < 3 Py_DECREF(int_value); return result; default: /* Should never happen...indicates an internal error: */ goto error; } } error: return raise_trait_error( trait, obj, name, value ); done: Py_INCREF( value ); done2: return value; } /*----------------------------------------------------------------------------- | Sets the value of the 'validate' field of a CTrait instance: +----------------------------------------------------------------------------*/ static trait_validate validate_handlers[] = { validate_trait_type, validate_trait_instance, #if PY_MAJOR_VERSION < 3 validate_trait_self_type, validate_trait_int, #else validate_trait_self_type, NULL, #endif // #if PY_MAJOR_VERSION < 3 validate_trait_float, validate_trait_enum, validate_trait_map, validate_trait_complex, NULL, validate_trait_tuple, validate_trait_prefix_map, validate_trait_coerce_type, validate_trait_cast_type, validate_trait_function, validate_trait_python, /* The following entries are used by the __getstate__ method... */ setattr_validate0, setattr_validate1, setattr_validate2, setattr_validate3, /* ...End of __getstate__ method entries */ validate_trait_adapt, validate_trait_integer, }; static PyObject * _trait_set_validate ( trait_object * trait, PyObject * args ) { PyObject * validate; PyObject * v1, * v2, * v3; int n, kind; if ( !PyArg_ParseTuple( args, "O", &validate ) ) return NULL; if ( PyCallable_Check( validate ) ) { kind = 14; goto done; } if ( PyTuple_CheckExact( validate ) ) { n = PyTuple_GET_SIZE( validate ); if ( n > 0 ) { kind = Py2to3_PyNum_AsLong( PyTuple_GET_ITEM( validate, 0 ) ); switch ( kind ) { case 0: /* Type check: */ if ( (n <= 3) && PyType_Check( PyTuple_GET_ITEM( validate, n - 1 ) ) && ((n == 2) || (PyTuple_GET_ITEM( validate, 1 ) == Py_None)) ) goto done; break; case 1: /* Instance check: */ if ( (n <= 3) && ((n == 2) || (PyTuple_GET_ITEM( validate, 1 ) == Py_None)) ) goto done; break; case 2: /* Self type check: */ if ( (n == 1) || ((n == 2) && (PyTuple_GET_ITEM( validate, 1 ) == Py_None)) ) goto done; break; #if PY_MAJOR_VERSION < 3 case 3: /* Integer range check: */ if ( n == 4 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); v2 = PyTuple_GET_ITEM( validate, 2 ); v3 = PyTuple_GET_ITEM( validate, 3 ); if ( ((v1 == Py_None) || PyInt_Check( v1 )) && ((v2 == Py_None) || PyInt_Check( v2 )) && PyInt_Check( v3 ) ) goto done; } break; #endif // #if PY_MAJOR_VERSION < 3 case 4: /* Floating point range check: */ if ( n == 4 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); v2 = PyTuple_GET_ITEM( validate, 2 ); v3 = PyTuple_GET_ITEM( validate, 3 ); if ( ((v1 == Py_None) || PyFloat_Check( v1 )) && ((v2 == Py_None) || PyFloat_Check( v2 )) && Py2to3_PyNum_Check( v3 ) ) goto done; } break; case 5: /* Enumerated item check: */ if ( n == 2 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); if ( PyTuple_CheckExact( v1 ) ) goto done; } break; case 6: /* Mapped item check: */ if ( n == 2 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); if ( PyDict_Check( v1 ) ) goto done; } break; case 7: /* TraitComplex item check: */ if ( n == 2 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); if ( PyTuple_CheckExact( v1 ) ) goto done; } break; /* case 8: 'Slow' validate check: */ case 9: /* TupleOf item check: */ if ( n == 2 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); if ( PyTuple_CheckExact( v1 ) ) goto done; } break; case 10: /* Prefix map item check: */ if ( n == 3 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); if ( PyDict_Check( v1 ) ) goto done; } break; case 11: /* Coercable type check: */ if ( n >= 2 ) goto done; break; case 12: /* Castable type check: */ if ( n == 2 ) goto done; break; case 13: /* Function validator check: */ if ( n == 2 ) { v1 = PyTuple_GET_ITEM( validate, 1 ); if ( PyCallable_Check( v1 ) ) goto done; } break; /* case 14: Python-based validator check: */ /* case 15..18: Property 'setattr' validate checks: */ case 19: /* PyProtocols 'adapt' check: */ /* Note: We don't check the 'class' argument (item[1]) because some old-style code creates classes that are not strictly classes or types (e.g. VTK), and yet they work correctly with the rest of the Instance code */ if ( (n == 4) && Py2to3_PyNum_Check( PyTuple_GET_ITEM( validate, 2 ) ) && PyBool_Check( PyTuple_GET_ITEM( validate, 3 ) ) ) { goto done; } break; case 20: /* Integer check: */ if ( n == 1 ) goto done; break; } } } PyErr_SetString( PyExc_ValueError, "The argument must be a tuple or callable." ); return NULL; done: trait->validate = validate_handlers[ kind ]; Py_INCREF( validate ); Py_XDECREF( trait->py_validate ); trait->py_validate = validate; Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Gets the value of the 'validate' field of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_get_validate ( trait_object * trait ) { if ( trait->validate != NULL ) { Py_INCREF( trait->py_validate ); return trait->py_validate; } Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Validates that a particular value can be assigned to an object trait: +----------------------------------------------------------------------------*/ static PyObject * _trait_validate ( trait_object * trait, PyObject * args ) { PyObject * object, * name, * value; if ( !PyArg_ParseTuple( args, "OOO", &object, &name, &value ) ) return NULL; if ( trait->validate == NULL ) { Py_INCREF( value ); return value; } return trait->validate( trait, (has_traits_object *)object, name, value ); } /*----------------------------------------------------------------------------- | Calls a Python-based trait post_setattr handler: +----------------------------------------------------------------------------*/ static int post_setattr_trait_python ( trait_object * trait, has_traits_object * obj, PyObject * name, PyObject * value ) { PyObject * result; PyObject * args = PyTuple_New( 3 ); if ( args == NULL ) return -1; Py_INCREF( obj ); Py_INCREF( name ); Py_INCREF( value ); PyTuple_SET_ITEM( args, 0, (PyObject *) obj ); PyTuple_SET_ITEM( args, 1, name ); PyTuple_SET_ITEM( args, 2, value ); result = PyObject_Call( trait->py_post_setattr, args, NULL ); Py_DECREF( args ); if ( result == NULL ) return -1; Py_DECREF( result ); return 0; } /*----------------------------------------------------------------------------- | Returns the various forms of delegate names: +----------------------------------------------------------------------------*/ static PyObject * delegate_attr_name_name ( trait_object * trait, has_traits_object * obj, PyObject * name ) { Py_INCREF( name ); return name; } static PyObject * delegate_attr_name_prefix ( trait_object * trait, has_traits_object * obj, PyObject * name ) { Py_INCREF( trait->delegate_prefix ); return trait->delegate_prefix; } static PyObject * delegate_attr_name_prefix_name ( trait_object * trait, has_traits_object * obj, PyObject * name ) { #if PY_MAJOR_VERSION < 3 char * p; int prefix_len = PyString_GET_SIZE( trait->delegate_prefix ); int name_len = PyString_GET_SIZE( name ); int total_len = prefix_len + name_len; PyObject * result = PyString_FromStringAndSize( NULL, total_len ); if ( result == NULL ) { Py_INCREF( Py_None ); return Py_None; } p = PyString_AS_STRING( result ); memcpy( p, PyString_AS_STRING( trait->delegate_prefix ), prefix_len ); memcpy( p + prefix_len, PyString_AS_STRING( name ), name_len ); #else PyObject *result = PyUnicode_Concat( trait->delegate_prefix, name ); #endif return result; } static PyObject * delegate_attr_name_class_name ( trait_object * trait, has_traits_object * obj, PyObject * name ) { PyObject * prefix, * result; #if PY_MAJOR_VERSION < 3 char * p; int prefix_len, name_len, total_len; #endif prefix = PyObject_GetAttr( (PyObject *) Py_TYPE(obj), class_prefix ); // fixme: Should verify that prefix is a string... if ( prefix == NULL ) { PyErr_Clear(); Py_INCREF( name ); return name; } #if PY_MAJOR_VERSION < 3 prefix_len = PyString_GET_SIZE( prefix ); name_len = PyString_GET_SIZE( name ); total_len = prefix_len + name_len; result = PyString_FromStringAndSize( NULL, total_len ); if ( result == NULL ) { Py_INCREF( Py_None ); return Py_None; } p = PyString_AS_STRING( result ); memcpy( p, PyString_AS_STRING( prefix ), prefix_len ); memcpy( p + prefix_len, PyString_AS_STRING( name ), name_len ); #else result = PyUnicode_Concat( prefix, name ); #endif Py_DECREF( prefix ); return result; } /*----------------------------------------------------------------------------- | Sets the value of the 'post_setattr' field of a CTrait instance: +----------------------------------------------------------------------------*/ static delegate_attr_name_func delegate_attr_name_handlers[] = { delegate_attr_name_name, delegate_attr_name_prefix, delegate_attr_name_prefix_name, delegate_attr_name_class_name, NULL }; static PyObject * _trait_delegate ( trait_object * trait, PyObject * args ) { PyObject * delegate_name; PyObject * delegate_prefix; int prefix_type; int modify_delegate; #if PY_MAJOR_VERSION < 3 { const char *delegate_name_str; const char *delegate_prefix_str; if ( !PyArg_ParseTuple( args, "ssii", &delegate_name_str, &delegate_prefix_str, &prefix_type, &modify_delegate ) ) return NULL; delegate_name = PyString_FromString(delegate_name_str); delegate_prefix = PyString_FromString(delegate_prefix_str); if(!delegate_name || !delegate_prefix){ Py_XDECREF(delegate_name); Py_XDECREF(delegate_prefix); return NULL; } } #else if ( !PyArg_ParseTuple( args, "UUii", &delegate_name, &delegate_prefix, &prefix_type, &modify_delegate ) ) return NULL; Py_INCREF( delegate_name ); Py_INCREF( delegate_prefix ); #endif if ( modify_delegate ) { trait->flags |= TRAIT_MODIFY_DELEGATE; } else { trait->flags &= (~TRAIT_MODIFY_DELEGATE); } trait->delegate_name = delegate_name; trait->delegate_prefix = delegate_prefix; if ( (prefix_type < 0) || (prefix_type > 3) ) prefix_type = 0; trait->delegate_attr_name = delegate_attr_name_handlers[ prefix_type ]; Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the value of the 'comparison' mode of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_rich_comparison ( trait_object * trait, PyObject * args ) { int compare_type; if ( !PyArg_ParseTuple( args, "i", &compare_type ) ) return NULL; trait->flags &= (~(TRAIT_NO_VALUE_TEST | TRAIT_OBJECT_IDENTITY)); if ( compare_type == 0 ) trait->flags |= TRAIT_OBJECT_IDENTITY; Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the appropriate value comparison mode flags of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_comparison_mode ( trait_object * trait, PyObject * args ) { int comparison_mode; if ( !PyArg_ParseTuple( args, "i", &comparison_mode ) ) return NULL; trait->flags &= (~(TRAIT_NO_VALUE_TEST | TRAIT_OBJECT_IDENTITY)); switch ( comparison_mode ) { case 0: trait->flags |= TRAIT_NO_VALUE_TEST; break; case 1: trait->flags |= TRAIT_OBJECT_IDENTITY; default: break; } Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the value of the 'value allowed' mode of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_value_allowed ( trait_object * trait, PyObject * args ) { int value_allowed; if ( !PyArg_ParseTuple( args, "i", &value_allowed ) ) return NULL; if ( value_allowed ) { trait->flags |= TRAIT_VALUE_ALLOWED; } else { trait->flags &= (~TRAIT_VALUE_ALLOWED); } Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the value of the 'value trait' mode of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_value_property ( trait_object * trait, PyObject * args ) { int value_trait; if ( !PyArg_ParseTuple( args, "i", &value_trait ) ) return NULL; if ( value_trait ) { trait->flags |= TRAIT_VALUE_PROPERTY; } else { trait->flags &= (~TRAIT_VALUE_PROPERTY); } Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the value of the 'setattr_original_value' flag of a CTrait instance: +----------------------------------------------------------------------------*/ static PyObject * _trait_setattr_original_value ( trait_object * trait, PyObject * args ) { int original_value; if ( !PyArg_ParseTuple( args, "i", &original_value ) ) return NULL; if ( original_value != 0 ) { trait->flags |= TRAIT_SETATTR_ORIGINAL_VALUE; } else { trait->flags &= (~TRAIT_SETATTR_ORIGINAL_VALUE); } Py_INCREF( trait ); return (PyObject *) trait; } /*----------------------------------------------------------------------------- | Sets the value of the 'post_setattr_original_value' flag of a CTrait | instance (used in the processing of 'post_settattr' calls): +----------------------------------------------------------------------------*/ static PyObject * _trait_post_setattr_original_value ( trait_object * trait, PyObject * args ) { int original_value; if ( !PyArg_ParseTuple( args, "i", &original_value ) ) return NULL; if ( original_value != 0 ) { trait->flags |= TRAIT_POST_SETATTR_ORIGINAL_VALUE; } else { trait->flags &= (~TRAIT_POST_SETATTR_ORIGINAL_VALUE); } Py_INCREF( trait ); return (PyObject *) trait; } /*----------------------------------------------------------------------------- | Sets the value of the 'is_mapped' flag of a CTrait instance (used in the | processing of the default value of a trait with a 'post_settattr' handler): +----------------------------------------------------------------------------*/ static PyObject * _trait_is_mapped ( trait_object * trait, PyObject * args ) { int is_mapped; if ( !PyArg_ParseTuple( args, "i", &is_mapped ) ) return NULL; if ( is_mapped != 0 ) { trait->flags |= TRAIT_IS_MAPPED; } else { trait->flags &= (~TRAIT_IS_MAPPED); } Py_INCREF( trait ); return (PyObject *) trait; } /*----------------------------------------------------------------------------- | Sets the 'property' value fields of a CTrait instance: +----------------------------------------------------------------------------*/ static trait_setattr setattr_property_handlers[] = { setattr_property0, setattr_property1, setattr_property2, setattr_property3, /* The following entries are used by the __getstate__ method__: */ (trait_setattr) post_setattr_trait_python, NULL }; static PyObject * _trait_property ( trait_object * trait, PyObject * args ) { PyObject * get, * set, * validate, * result, * temp; int get_n, set_n, validate_n; if ( PyTuple_GET_SIZE( args ) == 0 ) { if ( trait->flags & TRAIT_PROPERTY ) { result = PyTuple_New( 3 ); if ( result != NULL ) { PyTuple_SET_ITEM( result, 0, temp = trait->delegate_name ); Py_INCREF( temp ); PyTuple_SET_ITEM( result, 1, temp = trait->delegate_prefix ); Py_INCREF( temp ); PyTuple_SET_ITEM( result, 2, temp = trait->py_validate ); Py_INCREF( temp ); return result; } return NULL; } else { Py_INCREF( Py_None ); return Py_None; } } if ( !PyArg_ParseTuple( args, "OiOiOi", &get, &get_n, &set, &set_n, &validate, &validate_n ) ) return NULL; if ( !PyCallable_Check( get ) || !PyCallable_Check( set ) || ((validate != Py_None) && !PyCallable_Check( validate )) || (get_n < 0) || (get_n > 3) || (set_n < 0) || (set_n > 3) || (validate_n < 0) || (validate_n > 3) ) { PyErr_SetString( PyExc_ValueError, "Invalid arguments." ); return NULL; } trait->flags |= TRAIT_PROPERTY; trait->getattr = getattr_property_handlers[ get_n ]; if ( validate != Py_None ) { trait->setattr = setattr_validate_property; trait->post_setattr = (trait_post_setattr) setattr_property_handlers[ set_n ]; trait->validate = setattr_validate_handlers[ validate_n ]; } else trait->setattr = setattr_property_handlers[ set_n ]; trait->delegate_name = get; trait->delegate_prefix = set; trait->py_validate = validate; Py_INCREF( get ); Py_INCREF( set ); Py_INCREF( validate ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Clones one trait into another: +----------------------------------------------------------------------------*/ static void trait_clone ( trait_object * trait, trait_object * source ) { trait->flags = source->flags; trait->getattr = source->getattr; trait->setattr = source->setattr; trait->post_setattr = source->post_setattr; trait->py_post_setattr = source->py_post_setattr; trait->validate = source->validate; trait->py_validate = source->py_validate; trait->default_value_type = source->default_value_type; trait->default_value = source->default_value; trait->delegate_name = source->delegate_name; trait->delegate_prefix = source->delegate_prefix; trait->delegate_attr_name = source->delegate_attr_name; trait->handler = source->handler; Py_XINCREF( trait->py_post_setattr ); Py_XINCREF( trait->py_validate ); Py_XINCREF( trait->delegate_name ); Py_XINCREF( trait->default_value ); Py_XINCREF( trait->delegate_prefix ); Py_XINCREF( trait->handler ); } static PyObject * _trait_clone ( trait_object * trait, PyObject * args ) { trait_object * source; if ( !PyArg_ParseTuple( args, "O!", ctrait_type, &source ) ) return NULL; trait_clone( trait, source ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Returns (and optionally creates) the trait 'notifiers' list: +----------------------------------------------------------------------------*/ static PyObject * _trait_notifiers ( trait_object * trait, PyObject * args ) { PyObject * result; PyObject * list; int force_create; if ( !PyArg_ParseTuple( args, "i", &force_create ) ) return NULL; result = (PyObject *) trait->notifiers; if ( result == NULL ) { result = Py_None; if ( force_create && ((list = PyList_New( 0 )) != NULL) ) trait->notifiers = (PyListObject *) (result = list); } Py_INCREF( result ); return result; } /*----------------------------------------------------------------------------- | Converts a function to an index into a function table: +----------------------------------------------------------------------------*/ static int func_index ( void * function, void ** function_table ) { int i; for ( i = 0; function != function_table[i]; i++ ); return i; } /*----------------------------------------------------------------------------- | Gets the pickleable state of the trait: +----------------------------------------------------------------------------*/ static PyObject * _trait_getstate ( trait_object * trait, PyObject * args ) { PyObject * result; if ( !PyArg_ParseTuple( args, "" ) ) return NULL; result = PyTuple_New( 15 ); if ( result == NULL ) return NULL; PyTuple_SET_ITEM( result, 0, Py2to3_PyNum_FromLong( func_index( (void *) trait->getattr, (void **) getattr_handlers ) ) ); PyTuple_SET_ITEM( result, 1, Py2to3_PyNum_FromLong( func_index( (void *) trait->setattr, (void **) setattr_handlers ) ) ); PyTuple_SET_ITEM( result, 2, Py2to3_PyNum_FromLong( func_index( (void *) trait->post_setattr, (void **) setattr_property_handlers ) ) ); PyTuple_SET_ITEM( result, 3, get_callable_value( trait->py_post_setattr )); PyTuple_SET_ITEM( result, 4, Py2to3_PyNum_FromLong( func_index( (void *) trait->validate, (void **) validate_handlers ) ) ); PyTuple_SET_ITEM( result, 5, get_callable_value( trait->py_validate ) ); PyTuple_SET_ITEM( result, 6, Py2to3_PyNum_FromLong( trait->default_value_type ) ); PyTuple_SET_ITEM( result, 7, get_value( trait->default_value ) ); PyTuple_SET_ITEM( result, 8, Py2to3_PyNum_FromLong( trait->flags ) ); PyTuple_SET_ITEM( result, 9, get_value( trait->delegate_name ) ); PyTuple_SET_ITEM( result, 10, get_value( trait->delegate_prefix ) ); PyTuple_SET_ITEM( result, 11, Py2to3_PyNum_FromLong( func_index( (void *) trait->delegate_attr_name, (void **) delegate_attr_name_handlers ) ) ); PyTuple_SET_ITEM( result, 12, get_value( NULL ) ); /* trait->notifiers */ PyTuple_SET_ITEM( result, 13, get_value( trait->handler ) ); PyTuple_SET_ITEM( result, 14, get_value( trait->obj_dict ) ); return result; } /*----------------------------------------------------------------------------- | Restores the pickled state of the trait: +----------------------------------------------------------------------------*/ static PyObject * _trait_setstate ( trait_object * trait, PyObject * args ) { PyObject * ignore, * temp, *temp2; int getattr_index, setattr_index, post_setattr_index, validate_index, delegate_attr_name_index; if ( !PyArg_ParseTuple( args, "(iiiOiOiOiOOiOOO)", &getattr_index, &setattr_index, &post_setattr_index, &trait->py_post_setattr, &validate_index, &trait->py_validate, &trait->default_value_type, &trait->default_value, &trait->flags, &trait->delegate_name, &trait->delegate_prefix, &delegate_attr_name_index, &ignore, &trait->handler, &trait->obj_dict ) ) return NULL; trait->getattr = getattr_handlers[ getattr_index ]; trait->setattr = setattr_handlers[ setattr_index ]; trait->post_setattr = (trait_post_setattr) setattr_property_handlers[ post_setattr_index ]; trait->validate = validate_handlers[ validate_index ]; trait->delegate_attr_name = delegate_attr_name_handlers[ delegate_attr_name_index ]; /* Convert any references to callable methods on the handler back into bound methods: */ temp = trait->py_validate; if ( Py2to3_PyNum_Check( temp ) ) trait->py_validate = PyObject_GetAttrString( trait->handler, "validate" ); else if ( PyTuple_Check( temp ) && (Py2to3_PyNum_AsLong( PyTuple_GET_ITEM( temp, 0 ) ) == 10) ) { temp2 = PyObject_GetAttrString( trait->handler, "validate" ); Py_INCREF( temp2 ); Py_DECREF( PyTuple_GET_ITEM( temp, 2 ) ); PyTuple_SET_ITEM( temp, 2, temp2 ); } if ( Py2to3_PyNum_Check( trait->py_post_setattr ) ) trait->py_post_setattr = PyObject_GetAttrString( trait->handler, "post_setattr" ); Py_INCREF( trait->py_post_setattr ); Py_INCREF( trait->py_validate ); Py_INCREF( trait->default_value ); Py_INCREF( trait->delegate_name ); Py_INCREF( trait->delegate_prefix ); Py_INCREF( trait->handler ); Py_INCREF( trait->obj_dict ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Returns the current trait dictionary: +----------------------------------------------------------------------------*/ static PyObject * get_trait_dict ( trait_object * trait, void * closure ) { PyObject * obj_dict = trait->obj_dict; if ( obj_dict == NULL ) { trait->obj_dict = obj_dict = PyDict_New(); if ( obj_dict == NULL ) return NULL; } Py_INCREF( obj_dict ); return obj_dict; } /*----------------------------------------------------------------------------- | Sets the current trait dictionary: +----------------------------------------------------------------------------*/ static int set_trait_dict ( trait_object * trait, PyObject * value, void * closure ) { if ( !PyDict_Check( value ) ) return dictionary_error(); return set_value( &trait->obj_dict, value ); } /*----------------------------------------------------------------------------- | Returns the current trait handler (if any): +----------------------------------------------------------------------------*/ static PyObject * get_trait_handler ( trait_object * trait, void * closure ) { return get_value( trait->handler ); } /*----------------------------------------------------------------------------- | Sets the current trait dictionary: +----------------------------------------------------------------------------*/ static int set_trait_handler ( trait_object * trait, PyObject * value, void * closure ) { return set_value( &trait->handler, value ); } /*----------------------------------------------------------------------------- | Returns the current post_setattr (if any): +----------------------------------------------------------------------------*/ static PyObject * get_trait_post_setattr ( trait_object * trait, void * closure ) { return get_value( trait->py_post_setattr ); } /*----------------------------------------------------------------------------- | Sets the value of the 'post_setattr' field of a CTrait instance: +----------------------------------------------------------------------------*/ static int set_trait_post_setattr ( trait_object * trait, PyObject * value, void * closure ) { if ( !PyCallable_Check( value ) ) { PyErr_SetString( PyExc_ValueError, "The assigned value must be callable." ); return -1; } trait->post_setattr = post_setattr_trait_python; return set_value( &trait->py_post_setattr, value ); } /*----------------------------------------------------------------------------- | 'CTrait' instance methods: +----------------------------------------------------------------------------*/ static PyMethodDef trait_methods[] = { { "__getstate__", (PyCFunction) _trait_getstate, METH_VARARGS, PyDoc_STR( "__getstate__()" ) }, { "__setstate__", (PyCFunction) _trait_setstate, METH_VARARGS, PyDoc_STR( "__setstate__(state)" ) }, { "default_value", (PyCFunction) _trait_default_value, METH_VARARGS, PyDoc_STR( "default_value(default_value)" ) }, { "default_value_for", (PyCFunction) _trait_default_value_for, METH_VARARGS, PyDoc_STR( "default_value_for(object,name)" ) }, { "set_validate", (PyCFunction) _trait_set_validate, METH_VARARGS, PyDoc_STR( "set_validate(validate_function)" ) }, { "get_validate", (PyCFunction) _trait_get_validate, METH_NOARGS, PyDoc_STR( "get_validate()" ) }, { "validate", (PyCFunction) _trait_validate, METH_VARARGS, PyDoc_STR( "validate(object,name,value)" ) }, { "delegate", (PyCFunction) _trait_delegate, METH_VARARGS, PyDoc_STR( "delegate(delegate_name,prefix,prefix_type,modify_delegate)" ) }, { "rich_comparison", (PyCFunction) _trait_rich_comparison, METH_VARARGS, PyDoc_STR( "rich_comparison(rich_comparison_boolean)" ) }, { "comparison_mode", (PyCFunction) _trait_comparison_mode, METH_VARARGS, PyDoc_STR( "comparison_mode(comparison_mode_enum)" ) }, { "value_allowed", (PyCFunction) _trait_value_allowed, METH_VARARGS, PyDoc_STR( "value_allowed(value_allowed_boolean)" ) }, { "value_property", (PyCFunction) _trait_value_property, METH_VARARGS, PyDoc_STR( "value_property(value_trait_boolean)" ) }, { "setattr_original_value", (PyCFunction) _trait_setattr_original_value, METH_VARARGS, PyDoc_STR( "setattr_original_value(original_value_boolean)" ) }, { "post_setattr_original_value", (PyCFunction) _trait_post_setattr_original_value, METH_VARARGS, PyDoc_STR( "post_setattr_original_value(original_value_boolean)" ) }, { "is_mapped", (PyCFunction) _trait_is_mapped, METH_VARARGS, PyDoc_STR( "is_mapped(is_mapped_boolean)" ) }, { "property", (PyCFunction) _trait_property, METH_VARARGS, PyDoc_STR( "property([get,set,validate])" ) }, { "clone", (PyCFunction) _trait_clone, METH_VARARGS, PyDoc_STR( "clone(trait)" ) }, { "cast", (PyCFunction) _trait_cast, METH_VARARGS, PyDoc_STR( "cast(value)" ) }, { "_notifiers", (PyCFunction) _trait_notifiers, METH_VARARGS, PyDoc_STR( "_notifiers(force_create)" ) }, { NULL, NULL }, }; /*----------------------------------------------------------------------------- | 'CTrait' property definitions: +----------------------------------------------------------------------------*/ static PyGetSetDef trait_properties[] = { { "__dict__", (getter) get_trait_dict, (setter) set_trait_dict }, { "handler", (getter) get_trait_handler, (setter) set_trait_handler }, { "post_setattr", (getter) get_trait_post_setattr, (setter) set_trait_post_setattr }, { 0 } }; /*----------------------------------------------------------------------------- | 'CTrait' type definition: +----------------------------------------------------------------------------*/ static PyTypeObject trait_type = { PyVarObject_HEAD_INIT( DEFERRED_ADDRESS( &PyType_Type ), 0 ) "traits.ctraits.cTrait", sizeof( trait_object ), 0, (destructor) trait_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ (getattrofunc) trait_getattro, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc) trait_traverse, /* tp_traverse */ (inquiry) trait_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ trait_methods, /* tp_methods */ 0, /* tp_members */ trait_properties, /* tp_getset */ DEFERRED_ADDRESS( &PyBaseObject_Type ), /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ sizeof( trait_object ) - sizeof( PyObject * ), /* tp_dictoffset */ (initproc) trait_init, /* tp_init */ DEFERRED_ADDRESS( PyType_GenericAlloc ), /* tp_alloc */ DEFERRED_ADDRESS( PyType_GenericNew ) /* tp_new */ }; /*----------------------------------------------------------------------------- | Sets the global 'Undefined' and 'Uninitialized' values: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_undefined ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "OO", &Undefined, &Uninitialized ) ) return NULL; Py_INCREF( Undefined ); Py_INCREF( Uninitialized ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'TraitError' and 'DelegationError' exception types: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_exceptions ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "OO", &TraitError, &DelegationError ) ) return NULL; Py_INCREF( TraitError ); Py_INCREF( DelegationError ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'TraitListObject', TraitSetObject and 'TraitDictObject' | classes: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_list_classes ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "OOO", &TraitListObject, &TraitSetObject, &TraitDictObject ) ) return NULL; Py_INCREF( TraitListObject ); Py_INCREF( TraitSetObject ); Py_INCREF( TraitDictObject ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'TraitValue' class: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_value_class ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "O", &TraitValue ) ) return NULL; Py_INCREF( TraitValue ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'adapt' reference to the PyProtocols 'adapt' function: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_adapt ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "O", &adapt ) ) return NULL; Py_INCREF( adapt ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'validate_implements' reference to the Python level | function: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_validate_implements ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "O", &validate_implements ) ) return NULL; Py_INCREF( validate_implements ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'ctrait_type' class reference: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_ctrait ( PyObject * self, PyObject * args ) { if ( !PyArg_ParseTuple( args, "O", &ctrait_type ) ) return NULL; Py_INCREF( ctrait_type ); Py_INCREF( Py_None ); return Py_None; } /*----------------------------------------------------------------------------- | Sets the global 'trait_notification_handler' function, and returns the | previous value: +----------------------------------------------------------------------------*/ static PyObject * _ctraits_trait_notification_handler ( PyObject * self, PyObject * args ) { PyObject * result = _trait_notification_handler; if ( !PyArg_ParseTuple( args, "O", &_trait_notification_handler ) ) { return NULL; } if ( _trait_notification_handler == Py_None ) { _trait_notification_handler = NULL; } else { Py_INCREF( _trait_notification_handler ); } if ( result == NULL ) { Py_INCREF( Py_None ); result = Py_None; } return result; } /*----------------------------------------------------------------------------- | 'CTrait' instance methods: +----------------------------------------------------------------------------*/ static PyMethodDef ctraits_methods[] = { { "_undefined", (PyCFunction) _ctraits_undefined, METH_VARARGS, PyDoc_STR( "_undefined(Undefined,Uninitialized)" ) }, { "_exceptions", (PyCFunction) _ctraits_exceptions, METH_VARARGS, PyDoc_STR( "_exceptions(TraitError,DelegationError)" ) }, { "_list_classes", (PyCFunction) _ctraits_list_classes, METH_VARARGS, PyDoc_STR( "_list_classes(TraitListObject,TraitSetObject,TraitDictObject)" ) }, { "_value_class", (PyCFunction) _ctraits_value_class, METH_VARARGS, PyDoc_STR( "_value_class(TraitValue)" ) }, { "_adapt", (PyCFunction) _ctraits_adapt, METH_VARARGS, PyDoc_STR( "_adapt(PyProtocols._speedups.adapt)" ) }, { "_validate_implements", (PyCFunction) _ctraits_validate_implements, METH_VARARGS, PyDoc_STR( "_validate_implements(validate_implements)" )}, { "_ctrait", (PyCFunction) _ctraits_ctrait, METH_VARARGS, PyDoc_STR( "_ctrait(CTrait_class)" ) }, { "_trait_notification_handler", (PyCFunction) _ctraits_trait_notification_handler, METH_VARARGS, PyDoc_STR( "_trait_notification_handler(handler)" ) }, { NULL, NULL }, }; /*----------------------------------------------------------------------------- | Performs module and type initialization: +----------------------------------------------------------------------------*/ Py2to3_MOD_INIT(ctraits) { PyObject * tmp; /* Create the 'ctraits' module: */ PyObject * module; Py2to3_MOD_DEF( module, "ctraits", ctraits__doc__, ctraits_methods ); if ( module == NULL ) return Py2to3_MOD_ERROR_VAL; /* Create the 'CHasTraits' type: */ has_traits_type.tp_base = &PyBaseObject_Type; has_traits_type.tp_alloc = PyType_GenericAlloc; if ( PyType_Ready( &has_traits_type ) < 0 ) return Py2to3_MOD_ERROR_VAL; Py_INCREF( &has_traits_type ); if ( PyModule_AddObject( module, "CHasTraits", (PyObject *) &has_traits_type ) < 0 ) return Py2to3_MOD_ERROR_VAL; /* Create the 'CTrait' type: */ trait_type.tp_base = &PyBaseObject_Type; trait_type.tp_alloc = PyType_GenericAlloc; trait_type.tp_new = PyType_GenericNew; if ( PyType_Ready( &trait_type ) < 0 ) return Py2to3_MOD_ERROR_VAL; Py_INCREF( &trait_type ); if ( PyModule_AddObject( module, "cTrait", (PyObject *) &trait_type ) < 0 ) return Py2to3_MOD_ERROR_VAL; /* Create the 'HasTraitsMonitor' list: */ tmp = PyList_New( 0 ); Py_INCREF( tmp ); if ( PyModule_AddObject( module, "_HasTraits_monitors", (PyObject*) tmp) < 0 ) { return Py2to3_MOD_ERROR_VAL; } _HasTraits_monitors = tmp; /* Predefine a Python string == "__class_traits__": */ class_traits = Py2to3_SimpleString_FromString( "__class_traits__" ); /* Predefine a Python string == "__listener_traits__": */ listener_traits = Py2to3_SimpleString_FromString( "__listener_traits__" ); /* Predefine a Python string == "editor": */ editor_property = Py2to3_SimpleString_FromString( "editor" ); /* Predefine a Python string == "__prefix__": */ class_prefix = Py2to3_SimpleString_FromString( "__prefix__" ); /* Predefine a Python string == "trait_added": */ trait_added = Py2to3_SimpleString_FromString( "trait_added" ); /* Create an empty tuple: */ empty_tuple = PyTuple_New( 0 ); /* Create an empty dict: */ empty_dict = PyDict_New(); /* Create the 'is_callable' marker: */ is_callable = Py2to3_PyNum_FromLong( -1 ); return Py2to3_MOD_SUCCESS_VAL(module); } traits-4.6.0/traits/etsconfig/000077500000000000000000000000001300633736300163225ustar00rootroot00000000000000traits-4.6.0/traits/etsconfig/__init__.py000066400000000000000000000005341300633736300204350ustar00rootroot00000000000000#----------------------------------------------------------------------------- # # Copyright (c) 2007 by Enthought, Inc. # All rights reserved. # #----------------------------------------------------------------------------- """ Supports sharing settings across projects or programs on the same system. Part of the EnthoughtBase project. """ traits-4.6.0/traits/etsconfig/api.py000066400000000000000000000000611300633736300174420ustar00rootroot00000000000000from etsconfig import ETSConfig, ETSToolkitError traits-4.6.0/traits/etsconfig/etsconfig.py000066400000000000000000000404021300633736300206550ustar00rootroot00000000000000""" Enthought Tool Suite configuration information. """ # Standard library imports. import sys import os from os import path from contextlib import contextmanager class ETSToolkitError(RuntimeError): """ Error raised by issues importing ETS toolkits Attributes ---------- message : str The message detailing the error. toolkit : str or None The toolkit associated with the error. """ def __init__(self, message='', toolkit=None, *args): if not message and toolkit: message = "could not import toolkit '{0}'".format(toolkit) self.toolkit = toolkit self.message = message if message: if toolkit: args = (toolkit,) + args args = (message,) + args self.args = args class ETSConfig(object): """ Enthought Tool Suite configuration information. This class should not use ANY other package in the tool suite so that it will always work no matter which other packages are present. """ ########################################################################### # 'object' interface. ########################################################################### #### operator methods ##################################################### def __init__(self): """ Constructor. Note that this constructor can only ever be called from within this module, since we don't expose the class. """ # Shadow attributes for properties. self._application_data = None self._application_home = None self._company = None self._toolkit = None self._kiva_backend = None self._user_data = None return ########################################################################### # 'ETSConfig' interface. ########################################################################### #### properties ########################################################### def get_application_data(self, create=False): """ Return the application data directory path. Parameters ---------- create: bool Create the corresponding directory or not. Notes ----- - This is a directory that applications and packages can safely write non-user accessible data to i.e. configuration information, preferences etc. - Do not put anything in here that the user might want to navigate to e.g. projects, user data files etc. - The actual location differs between operating systems. """ if self._application_data is None: self._application_data = \ self._initialize_application_data(create=create) return self._application_data def _get_application_data(self): """ Property getter, see get_application_data's docstring. """ return self.get_application_data(create=True) def _set_application_data(self, application_data): """ Property setter. """ self._application_data = application_data return def get_application_home(self, create=False): """ Return the application home directory path. Parameters ---------- create: bool Create the corresponding directory or not. Note ---- - This is a directory named after the current, running application that imported this module that applications and packages can safely write non-user accessible data to i.e. configuration information, preferences etc. It is a sub-directory of self.application_data, named after the directory that contains the "main" python script that started the process. For example, if application foo is started with a script named "run.py" in a directory named "foo", then the application home would be: /foo, regardless of if it was launched with "python /run.py" or "cd ; python run.py" - This is useful for library modules used in apps that need to store state, preferences, etc. for the specific app only, and not for all apps which use that library module. If the library module uses ETSConfig.application_home, they can store prefs for the app all in one place and do not need to know the details of where each app might reside. - Do not put anything in here that the user might want to navigate to e.g. projects, user home files etc. - The actual location differs between operating systems. """ if self._application_home is None: self._application_home = path.join( self.get_application_data(create=create), self._get_application_dirname()) return self._application_home application_data = property(_get_application_data, _set_application_data) def _get_application_home(self): """ Property getter, see get_application_home's docstring. """ return self.get_application_home(create=True) def _set_application_home(self, application_home): """ Property setter. """ self._application_home = application_home return application_home = property(_get_application_home, _set_application_home) def _get_company(self): """ Property getter. """ if self._company is None: self._company = self._initialize_company() return self._company def _set_company(self, company): """ Property setter for the company name. """ self._company = company return company = property(_get_company, _set_company) @contextmanager def provisional_toolkit(self, toolkit): """ Perform an operation with toolkit provisionally set This sets the toolkit attribute of the ETSConfig object to the provided value. If the operation fails with an exception, the toolkit is reset to nothing. This method should only be called if the toolkit is not currently set. Parameters ---------- toolkit : string The name of the toolkit to provisionally use. Raises ------ ETSToolkitError If the toolkit attribute is already set, then an ETSToolkitError will be raised when entering the context manager. """ if self.toolkit: msg = "ETSConfig toolkit is already set to '{0}'" raise ETSToolkitError(msg.format(self.toolkit)) self.toolkit = toolkit try: yield except: # reset the toolkit state self._toolkit = '' raise def _get_toolkit(self): """ Property getter for the GUI toolkit. The value returned is, in order of preference: the value set by the application; the value specified by the 'ETS_TOOLKIT' environment variable; otherwise the empty string. """ if self._toolkit is None: self._toolkit = self._initialize_toolkit() return self._toolkit.split('.')[0] def _set_toolkit(self, toolkit): """ Property setter for the GUI toolkit. The toolkit can be set more than once, but only if it is the same one each time. An application that is written for a particular toolkit can explicitly set it before any other module that gets the value is imported. """ if self._toolkit and self._toolkit != toolkit: raise ValueError, "cannot set toolkit to %s because it has "\ "already been set to %s" % (toolkit, self._toolkit) self._toolkit = toolkit return toolkit = property(_get_toolkit, _set_toolkit) def _get_enable_toolkit(self): """ Deprecated: This property is no longer used. Property getter for the Enable backend. The value returned is, in order of preference: the value set by the application; the value specified by the 'ENABLE_TOOLKIT' environment variable; otherwise the empty string. """ from warnings import warn warn('Use of the enable_toolkit attribute is deprecated.') return self.toolkit def _set_enable_toolkit(self, toolkit): """ Deprecated. Property setter for the Enable toolkit. The toolkit can be set more than once, but only if it is the same one each time. An application that is written for a particular toolkit can explicitly set it before any other module that gets the value is imported. """ from warnings import warn warn('Use of the enable_toolkit attribute is deprecated.') return enable_toolkit = property(_get_enable_toolkit, _set_enable_toolkit) def _get_kiva_backend(self): """ Property getter for the Kiva backend. The value returned is dependent on the value of the toolkit property. If toolkit specifies a kiva backend using the extended syntax: [.] then the value of the property will be whatever was specified. Otherwise the value will be a reasonable default for the given enable backend. """ if self._toolkit is None: raise AttributeError, "The kiva_backend attribute is dependent on toolkit, which has not been set." if self._kiva_backend is None: try: self._kiva_backend = self._toolkit.split('.')[1] except IndexError: # Pick a reasonable default based on the toolkit if self.toolkit == "wx": self._kiva_backend = "quartz" if sys.platform == "darwin" else "image" elif self.toolkit == "qt4": self._kiva_backend = "image" elif self.toolkit == "pyglet": self._kiva_backend = "gl" else: self._kiva_backend = "image" return self._kiva_backend kiva_backend = property(_get_kiva_backend) def _get_user_data(self): """ Property getter. This is a directory that users can safely write user accessible data to i.e. user-defined functions, edited functions, etc. The actual location differs between operating systems. """ if self._user_data is None: self._user_data = self._initialize_user_data() return self._user_data def _set_user_data(self, user_data): """ Property setter. """ self._user_data = user_data return user_data = property(_get_user_data, _set_user_data) #### private methods ##################################################### # fixme: In future, these methods could allow the properties to be set # via the (as yet non-existent) preference/configuration mechanism. This # would allow configuration via (in order of precedence):- # # - a configuration file # - environment variables # - the command line def _get_application_dirname(self): """ Return the name of the directory (not a path) that the "main" Python script which started this process resides in, or "" if it could not be determined or is not appropriate. For example, if the script that started the current process was named "run.py" in a directory named "foo", and was launched with "python run.py", the name "foo" would be returned (this assumes the directory name is the name of the app, which seems to be as good of an assumption as any). """ dirname = "" main_mod = sys.modules.get('__main__', None) if main_mod is not None: if hasattr(main_mod, '__file__'): main_mod_file = path.abspath(main_mod.__file__) dirname = path.basename(path.dirname(main_mod_file)) return dirname def _initialize_application_data(self, create=True): """ Initializes the (default) application data directory. """ if sys.platform == 'win32': environment_variable = 'APPDATA' directory_name = self.company else: environment_variable = 'HOME' directory_name = '.' + self.company.lower() # Lookup the environment variable. parent_directory = os.environ.get(environment_variable, None) if parent_directory is None or parent_directory == '/root': import tempfile from warnings import warn parent_directory = tempfile.gettempdir() user = os.environ.get('USER', None) if user is not None: directory_name += "_%s" % user warn('Environment variable "%s" not set, setting home directory to %s' % \ (environment_variable, parent_directory)) application_data = os.path.join(parent_directory, directory_name) if create: # If a file already exists with this name then make sure that it is # a directory! if os.path.exists(application_data): if not os.path.isdir(application_data): raise ValueError('File "%s" already exists' % application_data) # Otherwise, create the directory. else: os.makedirs(application_data) return application_data def _initialize_company(self): """ Initializes the (default) company. """ return 'Enthought' def _initialize_toolkit(self): """ Initializes the toolkit. """ if self._toolkit is not None: toolkit = self._toolkit else: toolkit = os.environ.get('ETS_TOOLKIT', '') return toolkit def _initialize_user_data(self): """ Initializes the (default) user data directory. """ # We check what the os.path.expanduser returns parent_directory = os.path.expanduser('~') directory_name = self.company if sys.platform == 'win32': try: from win32com.shell import shell, shellcon # Due to the fact that the user's My Documents directory can # be in some pretty strange places, it's safest to just ask # Windows where it is. MY_DOCS = shellcon.CSIDL_PERSONAL parent_directory = shell.SHGetFolderPath(0, MY_DOCS, 0, 0) except ImportError: # But if they don't have pywin32 installed, just do it the # naive way... # Check if the usr_dir is C:\\John Doe\\Documents and Settings. # If yes, then we should modify the usr_dir to be 'My Documents'. # If no, then the user must have modified the os.environ # variables and the directory chosen is a desirable one. desired_dir = os.path.join(parent_directory, 'My Documents') if os.path.exists(desired_dir): parent_directory = desired_dir else: directory_name = directory_name.lower() # The final directory. usr_dir = os.path.join(parent_directory, directory_name) # If a file already exists with this name then make sure that it is # a directory! if os.path.exists(usr_dir): if not os.path.isdir(usr_dir): raise ValueError('File "%s" already exists' % usr_dir) # Otherwise, create the directory. else: os.makedirs(usr_dir) return usr_dir # We very purposefully only have one object and do not export the class. We # could have just made everything class methods, but that always seems a bit # gorpy, especially with properties etc. ETSConfig = ETSConfig() #### EOF ###################################################################### traits-4.6.0/traits/etsconfig/tests/000077500000000000000000000000001300633736300174645ustar00rootroot00000000000000traits-4.6.0/traits/etsconfig/tests/__init__.py000066400000000000000000000000001300633736300215630ustar00rootroot00000000000000traits-4.6.0/traits/etsconfig/tests/test_etsconfig.py000066400000000000000000000234231300633736300230620ustar00rootroot00000000000000""" Tests the 'ETSConfig' configuration object. """ # Standard library imports. import contextlib import os import shutil import sys import tempfile import time if sys.version_info[:2] == (2, 6): import unittest2 as unittest else: import unittest # Enthought library imports. from traits.etsconfig.etsconfig import ETSConfig, ETSToolkitError @contextlib.contextmanager def temporary_directory(): """ Context manager to create and clean up a temporary directory. """ temp_dir = tempfile.mkdtemp() try: yield temp_dir finally: shutil.rmtree(temp_dir) @contextlib.contextmanager def restore_mapping_entry(mapping, key): """ Context manager that restores a mapping entry to its previous state on exit. """ missing = object() old_value = mapping.get(key, missing) try: yield finally: if old_value is missing: mapping.pop(key, None) else: mapping[key] = old_value @contextlib.contextmanager def temporary_home_directory(): """ Context manager that temporarily remaps HOME / APPDATA to a temporary directory. """ # Use the same recipe as in ETSConfig._initialize_application_data # to determine the home directory. home_var = 'APPDATA' if sys.platform == 'win32' else 'HOME' with temporary_directory() as temp_home: with restore_mapping_entry(os.environ, home_var): os.environ[home_var] = temp_home yield @contextlib.contextmanager def mock_sys_argv(args): old_args = sys.argv sys.argv = args try: yield finally: sys.argv = old_args @contextlib.contextmanager def mock_os_environ(args): old_environ = os.environ os.environ = args try: yield finally: os.environ = old_environ class ETSConfigTestCase(unittest.TestCase): """ Tests the 'ETSConfig' configuration object. """ ########################################################################### # 'TestCase' interface. ########################################################################### #### public methods ####################################################### def setUp(self): """ Prepares the test fixture before each test method is called. """ # Make a fresh instance each time. self.ETSConfig = type(ETSConfig)() def run(self, result=None): # Extend TestCase.run to use a temporary home directory. with temporary_home_directory(): super(ETSConfigTestCase, self).run(result) ########################################################################### # 'ETSConfigTestCase' interface. ########################################################################### #### public methods ####################################################### def test_application_data(self): """ application data """ dirname = self.ETSConfig.application_data self.assertEqual(os.path.exists(dirname), True) self.assertEqual(os.path.isdir(dirname), True) return def test_set_application_data(self): """ set application data """ old = self.ETSConfig.application_data self.ETSConfig.application_data = 'foo' self.assertEqual('foo', self.ETSConfig.application_data) self.ETSConfig.application_data = old self.assertEqual(old, self.ETSConfig.application_data) return def test_application_data_is_idempotent(self): """ application data is idempotent """ # Just do the previous test again! self.test_application_data() self.test_application_data() return def test_write_to_application_data_directory(self): """ write to application data directory """ self.ETSConfig.company = 'Blah' dirname = self.ETSConfig.application_data path = os.path.join(dirname, 'dummy.txt') data = str(time.time()) f = open(path, 'w') f.write(data) f.close() self.assertEqual(os.path.exists(path), True) f = open(path) result = f.read() f.close() os.remove(path) self.assertEqual(data, result) return def test_default_company(self): """ default company """ self.assertEqual(self.ETSConfig.company, 'Enthought') return def test_set_company(self): """ set company """ old = self.ETSConfig.company self.ETSConfig.company = 'foo' self.assertEqual('foo', self.ETSConfig.company) self.ETSConfig.company = old self.assertEqual(old, self.ETSConfig.company) return def _test_default_application_home(self): """ application home """ # This test is only valid when run with the 'main' at the end of this # file: "python app_dat_locator_test_case.py", in which case the # app_name will be the directory this file is in ('tests'). app_home = self.ETSConfig.application_home (dirname, app_name) = os.path.split(app_home) self.assertEqual(dirname, self.ETSConfig.application_data) self.assertEqual(app_name, 'tests') def test_toolkit_environ(self): test_args = ['something'] test_environ = {'ETS_TOOLKIT': 'test'} with mock_sys_argv(test_args): with mock_os_environ(test_environ): toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, 'test') def test_toolkit_environ_missing(self): test_args = ['something'] test_environ = {} with mock_sys_argv(test_args): with mock_os_environ(test_environ): toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, '') def test_set_toolkit(self): test_args = [] test_environ = {'ETS_TOOLKIT': 'test_environ'} with mock_sys_argv(test_args): with mock_os_environ(test_environ): self.ETSConfig.toolkit = 'test_direct' toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, 'test_direct') def test_provisional_toolkit(self): test_args = [] test_environ = {} with mock_sys_argv(test_args): with mock_os_environ(test_environ): print repr(self.ETSConfig.toolkit) with self.ETSConfig.provisional_toolkit('test_direct'): toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, 'test_direct') # should stay set, since no exception raised toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, 'test_direct') def test_provisional_toolkit_exception(self): test_args = [] test_environ = {'ETS_TOOLKIT': ''} with mock_sys_argv(test_args): with mock_os_environ(test_environ): try: with self.ETSConfig.provisional_toolkit('test_direct'): toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, 'test_direct') raise ETSToolkitError("Test exception") except ETSToolkitError as exc: if not exc.message == "Test exception": raise # should be reset, since exception raised toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, '') def test_provisional_toolkit_already_set(self): test_args = [] test_environ = {'ETS_TOOLKIT': 'test_environ'} with mock_sys_argv(test_args): with mock_os_environ(test_environ): with self.assertRaises(ETSToolkitError): with self.ETSConfig.provisional_toolkit('test_direct'): pass # should come from the environment toolkit = self.ETSConfig.toolkit self.assertEqual(toolkit, 'test_environ') def test_user_data(self): """ user data """ dirname = self.ETSConfig.user_data self.assertEqual(os.path.exists(dirname), True) self.assertEqual(os.path.isdir(dirname), True) return def test_set_user_data(self): """ set user data """ old = self.ETSConfig.user_data self.ETSConfig.user_data = 'foo' self.assertEqual('foo', self.ETSConfig.user_data) self.ETSConfig.user_data = old self.assertEqual(old, self.ETSConfig.user_data) return def test_user_data_is_idempotent(self): """ user data is idempotent """ # Just do the previous test again! self.test_user_data() return def test_write_to_user_data_directory(self): """ write to user data directory """ self.ETSConfig.company = 'Blah' dirname = self.ETSConfig.user_data path = os.path.join(dirname, 'dummy.txt') data = str(time.time()) f = open(path, 'w') f.write(data) f.close() self.assertEqual(os.path.exists(path), True) f = open(path) result = f.read() f.close() os.remove(path) self.assertEqual(data, result) return # For running as an individual set of tests. if __name__ == '__main__': # Add the non-default test of application_home...non-default because it # must be run using this module as a script to be valid. suite = unittest.TestLoader().loadTestsFromTestCase(ETSConfigTestCase) suite.addTest(ETSConfigTestCase('_test_default_application_home')) unittest.TextTestRunner(verbosity=2).run(suite) #### EOF ###################################################################### traits-4.6.0/traits/has_dynamic_views.py000066400000000000000000000360631300633736300204170ustar00rootroot00000000000000#----------------------------------------------------------------------------- # # Copyright (c) 2006, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: Dave Peterson # #----------------------------------------------------------------------------- """ Provides a framework that assembles Traits UI Views at run time, when the view is requested, rather than at the time a class is written. This capability is particularly useful when the object being 'viewed' with a Traits UI is part of a plug-in application -- such as Envisage. In general, this capability allows: * The GUI for an object can be extendable by contributions other than from the original code writer. * The view can be dynamic in that the elements it is composed of can change each time it is requested. * Registration of a handler can be associated with the view contributions. Either the original object writer, or a contributor, can use this framework to declare one or more dynamic views that are composed of sub-elements that only need to exist at the time the view is requested. Users of this framework create a dynamic view by registering a DynamicView declaration. That declaration includes a name that forms the basis for the metadata attributes that are used to identify and order the desired view sub-elements into the view's composition. In addition, the declaration includes any data to be passed into the constructor of the dynamic view and the id that should be used to persist the user's customization of the view. Additionally, this framework allows sub-elements themselves to also be dynamically composed of further sub-elements. For example, a dynamic view could be composed of two sub-elements: 1. The first is a dynamically composed HFlow, which represents a toolbar that can be extended through contributions of toolbar buttons. 2. The second could be a dynamic tabset where each page is also a contribution. Programmers include dynamic sub-elements within their dynamic views by contributing a DynamicViewSubElement into that view. When the framework comes across this contribution while building the view, it replaces that DynamicViewSubElement with a fully initialized Traits ViewSubElement composed in a manner similar to how the elements of the View itself were composed. Each contribution to a dynamic view or sub-element must be an instance of a Traits ViewSubElement and must have associated metadata like the following for each dynamic view or sub-element it will display in: __order : A float value. The framework uses only ViewSubElements with this metadata instantiated when building the dynamic view or sub-element with the specified name. The elements are sorted by ascending order of this value using the standard list sort function. __priority : A float value. The framework resolves any overloading of an order value by picking the first element encountered that has the highest priority value. The other elements with the same view order are not displayed at all. In addition, dynamic view contributions can also provide a 'handler', which behaves like a normal Traits Handler. That is, it can contain methods that are called when model values change and can access the Traits UIInfo object representing the actual UI instances. To provide a handler, append the following metadata to your view sub-element: __handler : A HasTraits instance. The framework will connect listeners to call the handler methods as part of the handler for the dynamic view. """ from __future__ import absolute_import # Enthought library imports: from traitsui.delegating_handler import DelegatingHandler # Local imports: from .has_traits import HasTraits from .trait_types import Any, Bool, Dict, Instance, Str from traitsui.api import View, ViewSubElement, ViewElement # Set up a logger: import logging logger = logging.getLogger( __name__ ) #------------------------------------------------------------------------------- # 'DynamicViewSubElement' class: #------------------------------------------------------------------------------- class DynamicViewSubElement ( ViewSubElement ): """ Declares a dynamic sub-element of a dynamic view. """ #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- #-- Public 'DynamicViewSubElement' Interface ------------------------------- #: Keyword arguments passed in during construction of the actual #: ViewSubElement instance. keywords = Dict # FIXME: Should be the 'Class' trait but I couldn't get that to work. #: The class of the actual ViewSubElement we are dynamically creating. klass = Any #: The name of this dynamic sub-element. This controls the metadata #: names identifying the sub-elements that compose this element. name = Str #------------------------------------------------------------------------------- # 'DynamicView' class: #------------------------------------------------------------------------------- class DynamicView ( HasTraits ): """ Declares a dynamic view. """ #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- #-- Public 'DynamicView' Interface ----------------------------------------- #: The ID of the view. This is the ID that the view's preferences will be #: saved under. id = Str #: The name of the view. This is the name that should be requested when #: calling edit_traits() or configure_traits(). name = Str #: Keyword arguments passed in during construction of the actual view #: instance. keywords = Dict #: Indicates whether this view should be the default traits view for objects #: it is contributed to. use_as_default = Bool( False ) #------------------------------------------------------------------------------- # 'HasDynamicViews' class: #------------------------------------------------------------------------------- class HasDynamicViews ( HasTraits ): """ Provides of a framework that builds Traits UI Views at run time, when the view is requested, rather than at the time a class is written. """ #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- #-- Protected 'HasDynamicViews' Interface ---------------------------------- #: The registry of dynamic views. The key is the view name and the value #: is the declaration of the dynamic view. _dynamic_view_registry = Dict( Str, Instance( DynamicView ) ) #--------------------------------------------------------------------------- # 'HasTraits' interface: #--------------------------------------------------------------------------- #-- Public Interface ------------------------------------------------------- def trait_view ( self, name = None, view_element = None ): """ Gets or sets a ViewElement associated with an object's class. Extended here to build dynamic views and sub-elements. """ result = None # If a view element was passed instead of a name or None, do not handle # this as a request for a dynamic view and let the standard Traits # trait_view method be called with it. Otherwise, compose the dynamic # view here. if not isinstance( name, ViewElement ): # If this is a request for the default view, see if one of our # dynamic views should be the default view: if (view_element is None) and (name is None or len( name ) < 1): for dname, declaration in self._dynamic_view_registry.items(): if declaration.use_as_default: result = self._compose_dynamic_view( dname ) break # Otherwise, handle if this is a request for a dynamic view: elif ((view_element is None) and (name in self._dynamic_view_registry)): result = self._compose_dynamic_view(name) # If we haven't created a dynamic view so far, then do the standard # traits thing to retrieve the UI element: if result is None: result = super( HasDynamicViews, self ).trait_view( name, view_element ) return result #--------------------------------------------------------------------------- # 'HasDynamicViews' interface: #--------------------------------------------------------------------------- #-- Public Interface ------------------------------------------------------- def declare_dynamic_view ( self, declaration ): """ A convenience method to add a new dynamic view declaration to this instance. """ self._dynamic_view_registry[ declaration.name ] = declaration #-- Protected Interface ---------------------------------------------------- def _build_dynamic_sub_element ( self, definition, sub_elements ): """ Returns the fully composed ViewSubElement from the sub-element contributions to the dynamic sub-element identified by the definition. """ logger.debug( '\tBuilding dynamic sub-element [%s] with elements [%s]', definition.name, sub_elements ) return definition.klass( *sub_elements, **definition.keywords ) def _build_dynamic_view (self, declaration, sub_elements, handler ): """ Returns a Traits View representing the specified dynamic view composed out of the provided view sub-elements. Implemented as a separate method to allow implementors to override the way in which the instantiated view is configured. """ logger.debug( '\tBuilding dynamic view [%s] with elements [%s]', declaration.name, sub_elements ) return View( # The view id allows the user's customization of this view, if any, # to be persisted when the view is closed and then that persisted # configuration to be applied when the view is next shown: id = declaration.id, # Include the specified handler: handler = handler, # Build the view out of the sub-elements: *sub_elements, # Include the declaration's keywords. **declaration.keywords ) def _compose_dynamic_sub_element ( self, definition ): """ Returns a dynamic UI element composed from its contributed parts. """ logger.debug( 'Composing dynamic sub-element named [%s] for [%s]', definition.name, self ) # Retrieve the set of elements that make up this sub-element: elements = self._get_dynamic_elements( definition.name ) # Build the sub-element: return self._build_dynamic_sub_element( definition, elements ) def _compose_dynamic_view ( self, name ): """ Returns a dynamic view composed from its contributed parts. """ logger.debug( 'Composing dynamic view [%s] for [%s]', name, self ) # Retrieve the declaration of this dynamic view: declaration = self._dynamic_view_registry[ name ] # Retrieve the set of elements that make up the view: elements = self._get_dynamic_elements( declaration.name ) # Build a handler that delegates to the contribution handlers if any # exist: handler = None handlers = self._get_dynamic_handlers( declaration.name, elements ) if len( handlers ) > 0: handler = DelegatingHandler( sub_handlers = handlers ) # Build the view: return self._build_dynamic_view( declaration, elements, handler ) def _get_dynamic_elements ( self, name ): """ Returns a list of the current elements meant to go into the composition of a dynamic view or subelement with the specified name. """ # Determine the metadata names used to find the sub-elements included # within this dynamic element: name = name.replace(' ', '_') order_trait_name = '_%s_order' % name priority_trait_name = '_%s_priority' % name # Now find all of the current sub-elements that we will use when # composing our element: all_elements = [ self.trait_view( g ) for g in self.trait_views( klass = ViewSubElement ) ] elements = [ e for e in all_elements if hasattr( e, order_trait_name ) and (getattr( e, order_trait_name ) is not None) ] # Filter out any overridden elements. This means taking out the # element with the lower priority whenever two elements have the # same order value: filtered = {} for e in elements: order = getattr( e, order_trait_name ) priority = getattr( e, priority_trait_name ) or 0 current = filtered.setdefault( order, e ) if current is not e: current_priority = getattr( current, priority_trait_name ) if current_priority < priority: filtered[ order ] = e # Sort the contributed elements by their display ordering values: ordering = filtered.keys() ordering.sort() elements = [ filtered[ order ] for order in ordering ] # Replace any dynamic sub-element with their full composition. # NOTE: We can't do this in the override of 'trait_view' because # then we get into infinite loops when a dynamic view subelement is # found as a child: for i in range( len( elements ) ): if isinstance( elements[i], DynamicViewSubElement ): e = elements.pop( i ) composed = self._compose_dynamic_sub_element( e ) elements.insert( i, composed ) return elements def _get_dynamic_handlers( self, name, elements ): """ Return a list of the handlers associated with the current elements meant to go into the dynamic view of the specified name. """ # Determine the metadata name used to declare a handler: name = name.replace(' ', '_') handler_name = '_%s_handler' % name handlers = [ getattr(e, handler_name) for e in elements if hasattr( e, handler_name ) and (getattr( e, handler_name ) is not None) ] logger.debug( '\tFound sub-handlers: %s', handlers ) return handlers traits-4.6.0/traits/has_traits.py000066400000000000000000004500371300633736300170650ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Original Date: 06/21/2002 # # Rewritten as a C-based type extension: 06/21/2004 # #------------------------------------------------------------------------------ """ Defines the HasTraits class, along with several useful subclasses and associated metaclasses. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import, division import copy as copy_module import weakref import re import sys from types import FunctionType, MethodType from . import __version__ as TraitsVersion from .adaptation.adaptation_error import AdaptationError from .ctraits import CHasTraits, _HasTraits_monitors from .traits import (CTrait, ForwardProperty, Property, SpecialNames, Trait, TraitFactory, __newobj__, generic_trait, trait_factory) from .trait_types import Any, Bool, Disallow, Enum, Event, Python, This from .trait_notifiers import (ExtendedTraitChangeNotifyWrapper, FastUITraitChangeNotifyWrapper, NewTraitChangeNotifyWrapper, StaticAnyTraitChangeNotifyWrapper, StaticTraitChangeNotifyWrapper, TraitChangeNotifyWrapper) from .trait_handlers import TraitType from .trait_base import (Missing, SequenceTypes, TraitsCache, Undefined, add_article, is_none, not_event, not_false) from .trait_errors import TraitError from .protocols.advice import addClassAdvisor from .util.deprecated import deprecated #------------------------------------------------------------------------------- # Set CHECK_INTERFACES to one of the following values: # # - 0: Does not check to see if classes implement their declared interfaces. # - 1: Ensures that classes implement the interfaces they say they do, and # logs a warning if they don't. # - 2: Ensures that classes implement the interfaces they say they do, and # raises an InterfaceError if they don't. #------------------------------------------------------------------------------- CHECK_INTERFACES = 0 #------------------------------------------------------------------------------- # Deferred definitions: # # The following classes have a 'chicken and the egg' definition problem. They # require Traits to work, because they subclass Traits, but the Traits # meta-class programming support uses them, so Traits can't be subclassed # until they are defined. # # Note: We need to look at whether the Category support could be used to # allow us to implement this better. #------------------------------------------------------------------------------- class ViewElement ( object ): pass def ViewElements ( ): return None #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- WrapperTypes = ( StaticAnyTraitChangeNotifyWrapper, StaticTraitChangeNotifyWrapper ) if sys.version_info[0] >= 3: # in python 3, unbound methods do not exist anymore, they're just functions BoundMethodTypes = ( MethodType, ) UnboundMethodTypes = ( FunctionType, ) else: BoundMethodTypes = ( MethodType, ) UnboundMethodTypes = ( MethodType, ) FunctionTypes = ( FunctionType, ) # Class dictionary entries used to save trait, listener and view information and # definitions: BaseTraits = '__base_traits__' ClassTraits = '__class_traits__' PrefixTraits = '__prefix_traits__' ListenerTraits = '__listener_traits__' ViewTraits = '__view_traits__' InstanceTraits = '__instance_traits__' # The default Traits View name DefaultTraitsView = 'traits_view' # Trait types which cannot have default values CantHaveDefaultValue = ( 'event', 'delegate', 'constant' ) # An empty list EmptyList = [] # The trait types that should be copied last when doing a 'copy_traits': DeferredCopy = ( 'delegate', 'property' ) # Quick test for normal vs extended trait name extended_trait_pat = re.compile( r'.*[ :\+\-,\.\*\?\[\]]' ) # Generic 'Any' trait: any_trait = Any().as_ctrait() #------------------------------------------------------------------------------- # Creates a clone of a specified trait: #------------------------------------------------------------------------------- def _clone_trait ( clone, metadata = None ): """ Creates a clone of a specified trait. """ trait = CTrait( 0 ) trait.clone( clone ) if clone.__dict__ is not None: trait.__dict__ = clone.__dict__.copy() if metadata is not None: trait.__dict__.update( metadata ) return trait #------------------------------------------------------------------------------- # Gets the definition of a specified method (if any): #------------------------------------------------------------------------------- def _get_method ( cls, method ): result = getattr( cls, method, None ) if (result is not None) and is_unbound_method_type(result): return result return None if sys.version_info[0] >= 3: def _get_def ( class_name, class_dict, bases, method ): """ Gets the definition of a specified method (if any). """ if method[0:2] == '__': method = '_%s%s' % ( class_name, method ) result = class_dict.get( method ) if ((result is not None) and is_function_type(result) and (getattr( result, 'on_trait_change', None ) is None)): return result for base in bases: result = getattr( base, method, None ) if ((result is not None) and is_unbound_method_type(result) and \ (getattr( result, 'on_trait_change', None ) is None)): return result return None else: def _get_def ( class_name, class_dict, bases, method ): """ Gets the definition of a specified method (if any). """ if method[0:2] == '__': method = '_%s%s' % ( class_name, method ) result = class_dict.get( method ) if ((result is not None) and is_function_type(result) and (getattr( result, 'on_trait_change', None ) is None)): return result for base in bases: result = getattr( base, method, None ) if ((result is not None) and is_unbound_method_type(result) and \ (getattr( result.im_func, 'on_trait_change', None ) is None)): return result return None def is_cython_func_or_method(method): """ Test if the given input is a Cython method or function. """ # The only way to get the type from the method with str comparison ... return 'cython_function_or_method' in str(type(method)) def is_bound_method_type(method): """ Test if the given input is a Python method or a Cython method. """ return isinstance(method, BoundMethodTypes ) or is_cython_func_or_method(method) def is_unbound_method_type(method): """ Test if the given input is a Python method or a Cython method. """ return isinstance(method, UnboundMethodTypes ) or is_cython_func_or_method(method) def is_function_type(function): """ Test if the given input is a Python function or a Cython method. """ return isinstance(function, FunctionTypes ) or \ is_cython_func_or_method(function) #------------------------------------------------------------------------------- # Returns whether or not a specified value is serializable: #------------------------------------------------------------------------------- def _is_serializable ( value ): """ Returns whether or not a specified value is serializable. """ if isinstance( value, ( list, tuple ) ): for item in value: if not _is_serializable( item ): return False return True if isinstance( value, dict ): for name, item in value.items(): if ((not _is_serializable( name )) or (not _is_serializable( item ))): return False return True return ((not isinstance( value, HasTraits )) or value.has_traits_interface( ISerializable )) #------------------------------------------------------------------------------- # Returns a dictionary of potential 'Instance' or 'List(Instance)' handlers: #------------------------------------------------------------------------------- def _get_instance_handlers ( class_dict, bases ): """ Returns a dictionary of potential 'Instance' or 'List(Instance)' handlers. """ # Create the results dictionary: instance_traits = {} # Merge all of the base class information into the result: for base in bases: for name, base_arg_lists in base.__dict__.get( InstanceTraits ).items(): arg_lists = instance_traits.get( name ) if arg_lists is None: instance_traits[ name ] = base_arg_lists[:] else: for arg_list in base_arg_lists: if arg_list not in arg_lists: arg_lists.append( arg_list ) # Merge in the information from the class dictionary: for name, value in class_dict.items(): if (name[:1] == '_') and is_function_type(value): n = 13 col = name.find( '_changed_for_' ) if col < 2: n = 11 col = name.find( '_fired_for_' ) if col >= 2: key = name[ col + n: ] if key != '': arg_list = ( name, name[ 1: col ] ) arg_lists = instance_traits.setdefault( key, [] ) if arg_list not in arg_lists: arg_lists.append( arg_list ) # Return the dictionary of possible arg_lists: return instance_traits #------------------------------------------------------------------------------- # Returns the correct 'delegate' listener pattern for a specified name and # delegate trait: #------------------------------------------------------------------------------- def get_delegate_pattern ( name, trait ): """ Returns the correct 'delegate' listener pattern for a specified name and delegate trait. """ prefix = trait._prefix if prefix == '': prefix = name elif (len( prefix ) > 1) and (prefix[-1] == '*'): prefix = prefix[:-1] + name return ' %s:%s' % ( trait._delegate, prefix ) #------------------------------------------------------------------------------- # '_SimpleTest' class: #------------------------------------------------------------------------------- class _SimpleTest: def __init__ ( self, value ): self.value = value def __call__ ( self, test ): return test == self.value #------------------------------------------------------------------------------- # Returns either the original value or a valid CTrait if the value can be # converted to a CTrait: #------------------------------------------------------------------------------- def _check_trait ( trait ): """ Returns either the original value or a valid CTrait if the value can be converted to a CTrait. """ if isinstance( trait, CTrait ): return trait if isinstance( trait, TraitFactory ): return trait_factory( trait ) if isinstance( trait, type ) and issubclass( trait, TraitType ): trait = trait() if isinstance( trait, TraitType ): return trait.as_ctrait() return trait #------------------------------------------------------------------------------- # Returns the trait corresponding to a specified value: #------------------------------------------------------------------------------- def _trait_for ( trait ): """ Returns the trait corresponding to a specified value. """ trait = _check_trait( trait ) if isinstance( trait, CTrait ): return trait return Trait( trait ) #------------------------------------------------------------------------------- # Returns the 'mapped trait' definition for a mapped trait: #------------------------------------------------------------------------------- def _mapped_trait_for ( trait ): """ Returns the 'mapped trait' definition for a mapped trait. """ default_value = trait.default_value()[1] try: default_value = trait.handler.mapped_value( default_value ) except: pass return Any( default_value, is_base = False, transient = True, editable = False ).as_ctrait() #------------------------------------------------------------------------------- # Adds a list of handlers to a specified notifiers list: #------------------------------------------------------------------------------- def _add_notifiers ( notifiers, handlers ): """ Adds a list of handlers to a specified notifiers list. """ for handler in handlers: if not isinstance( handler, WrapperTypes ): handler = StaticTraitChangeNotifyWrapper( handler ) notifiers.append( handler ) #------------------------------------------------------------------------------- # Adds any specified event handlers defined for a trait by a class: #------------------------------------------------------------------------------- def _add_event_handlers ( trait, cls, handlers ): """ Adds any specified event handlers defined for a trait by a class. """ events = trait.event if events is not None: if isinstance(events, basestring): events = [ events ] for event in events: handlers.append( _get_method( cls, '_%s_changed' % event ) ) handlers.append( _get_method( cls, '_%s_fired' % event ) ) #------------------------------------------------------------------------------- # Returns the method associated with a particular class property getter/setter: #------------------------------------------------------------------------------- def _property_method ( class_dict, name ): """ Returns the method associated with a particular class property getter/setter. """ return class_dict.get( name ) #------------------------------------------------------------------------------- # 'MetaHasTraits' class: #------------------------------------------------------------------------------- # This really should be 'HasTraits', but it's not defined yet: _HasTraits = None class MetaHasTraits ( type ): ### JMS: Need a docstring here. # All registered class creation listeners. # # { Str class_name : Callable listener } _listeners = {} def __new__ ( cls, class_name, bases, class_dict ): mhto = MetaHasTraitsObject( cls, class_name, bases, class_dict, False ) # Finish building the class using the updated class dictionary: klass = type.__new__( cls, class_name, bases, class_dict ) # Fix up all self referential traits to refer to this class: for trait in mhto.self_referential: trait.set_validate( ( 11, klass ) ) # Call all listeners that registered for this specific class: name = '%s.%s' % ( klass.__module__, klass.__name__ ) for listener in MetaHasTraits._listeners.get( name, [] ): listener( klass ) # Call all listeners that registered for ANY class: for listener in MetaHasTraits._listeners.get( '', [] ): listener( klass ) return klass def add_listener ( cls, listener, class_name = '' ): """ Adds a class creation listener. If the class name is the empty string then the listener will be called when *any* class is created. """ MetaHasTraits._listeners.setdefault( class_name, [] ).append( listener ) add_listener = classmethod( add_listener ) def remove_listener ( cls, listener, class_name = '' ): """ Removes a class creation listener. """ MetaHasTraits._listeners[ class_name ].remove( listener ) remove_listener = classmethod( remove_listener ) #------------------------------------------------------------------------------- # 'MetaHasTraitsObject' class: #------------------------------------------------------------------------------- class MetaHasTraitsObject ( object ): """ Performs all of the meta-class processing needed to convert any subclass of HasTraits into a well-formed traits class. """ def __init__ ( self, cls, class_name, bases, class_dict, is_category ): """ Processes all of the traits related data in the class dictionary. """ # Create the various class dictionaries, lists and objects needed to # hold trait and view information and definitions: base_traits = {} class_traits = {} prefix_traits = {} listeners = {} prefix_list = [] override_bases = bases view_elements = ViewElements() self_referential = [] # Create a list of just those base classes that derive from HasTraits: hastraits_bases = [ base for base in bases if base.__dict__.get( ClassTraits ) is not None ] # Create a list of all inherited trait dictionaries: inherited_class_traits = [ base.__dict__.get( ClassTraits ) for base in hastraits_bases ] # Move all trait definitions from the class dictionary to the # appropriate trait class dictionaries: for name, value in class_dict.items(): value = _check_trait( value ) rc = isinstance( value, CTrait ) if (not rc) and isinstance( value, ForwardProperty ): rc = True getter = _property_method( class_dict, '_get_' + name ) setter = _property_method( class_dict, '_set_' + name ) if (setter is None) and (getter is not None): if getattr( getter, 'settable', False ): setter = HasTraits._set_traits_cache elif getattr( getter, 'flushable', False ): setter = HasTraits._flush_traits_cache validate = _property_method( class_dict, '_validate_' + name ) if validate is None: validate = value.validate value = Property( getter, setter, validate, True, value.handler, **value.metadata ) if rc: del class_dict[ name ] if name[-1:] != '_': base_traits[ name ] = class_traits[ name ] = value value_type = value.type if value_type == 'trait': handler = value.handler if handler is not None: if handler.has_items: items_trait = _clone_trait( handler.items_event(), value.__dict__ ) if items_trait.instance_handler == \ '_list_changed_handler': items_trait.instance_handler = \ '_list_items_changed_handler' class_traits[ name + '_items' ] = items_trait if handler.is_mapped: class_traits[ name + '_' ] = _mapped_trait_for( value ) if isinstance( handler, This ): handler.info_text = \ add_article( class_name ) + ' instance' self_referential.append( value ) elif value_type == 'delegate': # Only add a listener if the trait.listenable metadata # is not False: if value._listenable is not False: listeners[ name ] = ( 'delegate', get_delegate_pattern( name, value ) ) elif value_type == 'event': on_trait_change = value.on_trait_change if isinstance( on_trait_change, basestring ): listeners[ name ] = ( 'event', on_trait_change ) else: name = name[:-1] prefix_list.append( name ) prefix_traits[ name ] = value elif isinstance( value, FunctionType ) or is_cython_func_or_method(value): pattern = getattr( value, 'on_trait_change', None ) if pattern is not None: listeners[ name ] = ( 'method', pattern ) elif isinstance( value, property ): class_traits[ name ] = generic_trait # Handle any view elements found in the class: elif isinstance( value, ViewElement ): # Add the view element to the class's 'ViewElements' if it is # not already defined (duplicate definitions are errors): if name in view_elements.content: raise TraitError( "Duplicate definition for view element '%s'" % name ) view_elements.content[ name ] = value # Replace all substitutable view sub elements with 'Include' # objects, and add the substituted items to the # 'ViewElements': value.replace_include( view_elements ) # Remove the view element from the class definition: del class_dict[ name ] else: for ct in inherited_class_traits: if name in ct: # The subclass is providing a default value for the # trait defined in a superclass. ictrait = ct[ name ] if ictrait.type in CantHaveDefaultValue: raise TraitError( "Cannot specify a default value " "for the %s trait '%s'. You must override the " "the trait definition instead." % ( ictrait.type, name ) ) default_value = value class_traits[ name ] = value = ictrait( default_value ) # Make sure that the trait now has the default value # has the correct initializer. value.default_value(1, value.default) del class_dict[ name ] override_bases = [] handler = value.handler if (handler is not None) and handler.is_mapped: class_traits[ name + '_' ] = _mapped_trait_for( value ) break # Process all HasTraits base classes: migrated_properties = {} implements = [] for base in hastraits_bases: base_dict = base.__dict__ # Merge listener information: for name, value in base_dict.get( ListenerTraits ).items(): if (name not in class_traits) and (name not in class_dict): listeners[ name ] = value # Merge base traits: for name, value in base_dict.get( BaseTraits ).items(): if name not in base_traits: property_info = value.property() if property_info is not None: key = id( value ) migrated_properties[ key ] = value = \ self.migrate_property( name, value, property_info, class_dict ) base_traits[ name ] = value elif is_category: raise TraitError, ("Cannot override '%s' trait " "definition in a category" % name) # Merge class traits: for name, value in base_dict.get( ClassTraits ).items(): if name not in class_traits: property_info = value.property() if property_info is not None: new_value = migrated_properties.get( id( value ) ) if new_value is not None: value = new_value else: value = self.migrate_property( name, value, property_info, class_dict ) class_traits[ name ] = value elif is_category: raise TraitError, ("Cannot override '%s' trait " "definition in a category" % name) # Merge prefix traits: base_prefix_traits = base_dict.get( PrefixTraits ) for name in base_prefix_traits['*']: if name not in prefix_list: prefix_list.append( name ) prefix_traits[ name ] = base_prefix_traits[ name ] elif is_category: raise TraitError, ("Cannot override '%s_' trait " "definition in a category" % name) # If the base class has a 'ViewElements' object defined, add it to # the 'parents' list of this class's 'ViewElements': parent_view_elements = base_dict.get( ViewTraits ) if parent_view_elements is not None: view_elements.parents.append( parent_view_elements ) # Make sure there is a definition for 'undefined' traits: if (prefix_traits.get( '' ) is None) and (not is_category): prefix_list.append( '' ) prefix_traits[''] = Python().as_ctrait() # Save a link to the prefix_list: prefix_traits['*'] = prefix_list # Make sure the trait prefixes are sorted longest to shortest # so that we can easily bind dynamic traits to the longest matching # prefix: prefix_list.sort( key = lambda x: -len(x) ) # Get the list of all possible 'Instance'/'List(Instance)' handlers: instance_traits = _get_instance_handlers( class_dict, hastraits_bases ) # If there is an 'anytrait_changed' event handler, wrap it so that # it can be attached to all traits in the class: anytrait = _get_def( class_name, class_dict, bases, '_anytrait_changed' ) if anytrait is not None: anytrait = StaticAnyTraitChangeNotifyWrapper( anytrait ) # Save it in the prefix traits dictionary so that any dynamically # created traits (e.g. 'prefix traits') can re-use it: prefix_traits['@'] = anytrait # Make one final pass over the class traits dictionary, making sure # all static trait notification handlers are attached to a 'cloned' # copy of the original trait: cloned = set() for name in class_traits.keys(): trait = class_traits[ name ] handlers = [ anytrait, _get_def( class_name, class_dict, bases, '_%s_changed' % name ), _get_def( class_name, class_dict, bases, '_%s_fired' % name ) ] # Check for an 'Instance' or 'List(Instance)' trait with defined # handlers: instance_handler = trait.instance_handler if ((instance_handler is not None) and (name in instance_traits) or ((instance_handler == '_list_items_changed_handler') and (name[-6:] == '_items') and (name[:-6] in instance_traits))): handlers.append( getattr( HasTraits, instance_handler ) ) events = trait.event if events is not None: if isinstance(events, basestring): events = [ events ] for event in events: handlers.append( _get_def( class_name, class_dict, bases, '_%s_changed' % event ) ) handlers.append( _get_def( class_name, class_dict, bases, '_%s_fired' % event ) ) handlers = [ h for h in handlers if h is not None ] default = _get_def( class_name, class_dict, [], '_%s_default' % name ) if (len( handlers ) > 0) or (default is not None): if name not in cloned: cloned.add( name ) class_traits[ name ] = trait = _clone_trait( trait ) if len( handlers ) > 0: _add_notifiers( trait._notifiers( 1 ), handlers ) if default is not None: trait.default_value( 8, default ) # Handle the case of properties whose value depends upon the value # of other traits: if (trait.type == 'property') and (trait.depends_on is not None): cached = trait.cached if cached is True: cached = TraitsCache + name depends_on = trait.depends_on if isinstance( depends_on, SequenceTypes ): depends_on = ','.join( depends_on ) else: # Note: We add the leading blank to force it to be treated # as using the extended trait notation so that it will # automatically add '_items' listeners to lists/dicts: depends_on = ' ' + depends_on listeners[ name ] = ( 'property', cached, depends_on ) # Save the list of self referential traits: self.self_referential = self_referential # Add the traits meta-data to the class: self.add_traits_meta_data( bases, class_dict, base_traits, class_traits, instance_traits, prefix_traits, listeners, view_elements ) #--------------------------------------------------------------------------- # Adds the traits meta-data to the class: #--------------------------------------------------------------------------- def add_traits_meta_data ( self, bases, class_dict, base_traits, class_traits, instance_traits, prefix_traits, listeners, view_elements ): """ Adds the Traits metadata to the class dictionary. """ class_dict[ BaseTraits ] = base_traits class_dict[ ClassTraits ] = class_traits class_dict[ InstanceTraits ] = instance_traits class_dict[ PrefixTraits ] = prefix_traits class_dict[ ListenerTraits ] = listeners class_dict[ ViewTraits ] = view_elements #--------------------------------------------------------------------------- # Migrates an existing property to the class being defined (allowing for # method overrides): #--------------------------------------------------------------------------- def migrate_property ( self, name, property, property_info, class_dict ): """ Migrates an existing property to the class being defined (allowing for method overrides). """ get = _property_method( class_dict, '_get_' + name ) set = _property_method( class_dict, '_set_' + name ) val = _property_method( class_dict, '_validate_' + name ) if ((get is not None) or (set is not None) or (val is not None)): old_get, old_set, old_val = property_info return Property( get or old_get, set or old_set, val or old_val, True, **property.__dict__ ) return property #------------------------------------------------------------------------------- # Manages the list of trait instance monitors: #------------------------------------------------------------------------------- def _trait_monitor_index ( cls, handler ): global _HasTraits_monitors type_handler = type( handler ) for i, _cls, _handler in enumerate( _HasTraits_monitors ): if type_handler is type( _handler ): if (((type_handler is MethodType) or 'cython_function_or_method' in str(type_handler)) and \ (handler.im_self is not None)): if ((handler.__name__ == _handler.__name__) and (handler.im_self is _handler.im_self)): return i elif handler == _handler: return i return -1 #------------------------------------------------------------------------------- # 'HasTraits' decorators: #------------------------------------------------------------------------------- def on_trait_change ( name, post_init = False, dispatch = 'same' ): """ Marks the following method definition as being a handler for the extended trait change specified by *name(s)*. Refer to the documentation for the on_trait_change() method of the **HasTraits** class for information on the correct syntax for the *name* argument and the semantics of the *dispatch* keyword argument. A handler defined using this decorator is normally effective immediately. However, if *post_init* is **True**, then the handler only becomes effective after all object constructor arguments have been processed. That is, trait values assigned as part of object construction will not cause the handler to be invoked. """ def decorator ( function ): function.on_trait_change = {'pattern': name, 'post_init': post_init, 'dispatch': dispatch} return function return decorator def cached_property ( function ): """ Marks the following method definition as being a "cached property". That is, it is a property getter which, for performance reasons, caches its most recently computed result in an attribute whose name is of the form: *_traits_cache_name*, where *name* is the name of the property. A method marked as being a cached property needs only to compute and return its result. The @cached_property decorator automatically wraps the decorated method in cache management code, eliminating the need to write boilerplate cache management code explicitly. For example:: file_name = File file_contents = Property( depends_on = 'file_name' ) @cached_property def _get_file_contents(self): fh = open(self.file_name, 'rb') result = fh.read() fh.close() return result In this example, accessing the *file_contents* trait calls the _get_file_contents() method only once each time after the **file_name** trait is modified. In all other cases, the cached value **_file_contents**, which maintained by the @cached_property wrapper code, is returned. Note the use, in the example, of the **depends_on** metadata attribute to specify that the value of **file_contents** depends on **file_name**, so that _get_file_contents() is called only when **file_name** changes. For details, see the traits.traits.Property() function. """ name = TraitsCache + function.__name__[ 5: ] def decorator ( self ): result = self.__dict__.get( name, Undefined ) if result is Undefined: self.__dict__[ name ] = result = function( self ) return result decorator.cached_property = True return decorator def property_depends_on ( dependency, settable = False, flushable = False ): """ Marks the following method definition as being a "cached property" that depends on the specified extended trait names. That is, it is a property getter which, for performance reasons, caches its most recently computed result in an attribute whose name is of the form: *_traits_cache_name*, where *name* is the name of the property. A method marked as being a cached property needs only to compute and return its result. The @property_depends_on decorator automatically wraps the decorated method in cache management code that will cache the most recently computed value and flush the cache when any of the specified dependencies are modified, thus eliminating the need to write boilerplate cache management code explicitly. For example:: file_name = File file_contents = Property @property_depends_on( 'file_name' ) def _get_file_contents(self): fh = open(self.file_name, 'rb') result = fh.read() fh.close() return result In this example, accessing the *file_contents* trait calls the _get_file_contents() method only once each time after the **file_name** trait is modified. In all other cases, the cached value **_file_contents**, which is maintained by the @cached_property wrapper code, is returned. """ def decorator ( function ): name = TraitsCache + function.__name__[ 5: ] def wrapper ( self ): result = self.__dict__.get( name, Undefined ) if result is Undefined: self.__dict__[ name ] = result = function( self ) return result wrapper.cached_property = True wrapper.depends_on = dependency wrapper.settable = settable wrapper.flushable = flushable return wrapper return decorator def weak_arg(arg): """ Create a weak reference to arg and wrap the function so that the dereferenced weakref is passed as the first argument. If arg has been deleted then the function is not called. """ # Create the weak reference weak_arg = weakref.ref(arg) def decorator(function): # We need multiple wrappers to traits can find the number of arguments. # The all just dereference the weak reference and the call the # function if it is not None. def wrapper0(): arg = weak_arg() if arg is not None: return function(arg) def wrapper1(arg1): arg = weak_arg() if arg is not None: return function(arg, arg1) def wrapper2(arg1, arg2): arg = weak_arg() if arg is not None: return function(arg, arg1, arg2) def wrapper3(arg1, arg2, arg3): arg = weak_arg() if arg is not None: return function(arg, arg1, arg2, arg3) def wrapper4(arg1, arg2, arg3, arg4): arg = weak_arg() if arg is not None: return function(arg, arg1, arg2, arg3, arg4) def wrappern(*args): arg = weak_arg() if arg is not None: function(arg, *args) # Return the correct wrapper depending on the arg count args = function.func_code.co_argcount-1 if args == 0: return wrapper0 elif args == 1: return wrapper1 elif args == 2: return wrapper2 elif args == 3: return wrapper3 elif args == 4: return wrapper4 else: return wrappern return decorator #------------------------------------------------------------------------------- # 'HasTraits' class: #------------------------------------------------------------------------------- class HasTraits ( CHasTraits ): """ Enables any Python class derived from it to have trait attributes. Most of the methods of HasTraits operated by default only on the trait attributes explicitly defined in the class definition. They do not operate on trait attributes defined by way of wildcards or by calling **add_trait()**. For example:: >>>class Person(HasTraits): ... name = Str ... age = Int ... temp_ = Any >>>bob = Person() >>>bob.temp_lunch = 'sandwich' >>>bob.add_trait('favorite_sport', Str('football')) >>>print bob.trait_names() ['trait_added', 'age', 'name'] In this example, the trait_names() method returns only the *age* and *name* attributes defined on the Person class. (The **trait_added** attribute is an explicit trait event defined on the HasTraits class.) The wildcard attribute *temp_lunch* and the dynamically-added trait attribute *favorite_sport* are not listed. """ __metaclass__ = MetaHasTraits #-- Trait Prefix Rules ----------------------------------------------------- #: Make traits 'property cache' values private with no type checking: _traits_cache__ = Any( private = True, transient = True ) #-- Class Variables -------------------------------------------------------- #: Mapping from dispatch type to notification wrapper class type wrappers = { 'same': TraitChangeNotifyWrapper, 'extended': ExtendedTraitChangeNotifyWrapper, 'new': NewTraitChangeNotifyWrapper, 'fast_ui': FastUITraitChangeNotifyWrapper, 'ui': FastUITraitChangeNotifyWrapper } #-- Trait Definitions ------------------------------------------------------ #: An event fired when a new trait is dynamically added to the object trait_added = Event( basestring ) #: An event that can be fired to indicate that the state of the object has #: been modified trait_modified = Event #--------------------------------------------------------------------------- # Handles a 'trait_added' event being fired: #--------------------------------------------------------------------------- def _trait_added_changed ( self, name ): """ Handles a 'trait_added' event being fired. """ # fixme: This test should be made more comprehensive by also verifying # that if the trait name does end in '_items', its base trait is also # a list or dictionary (in order to eliminate a false positive on an # unfortunately named trait: trait = self.trait( name ) if (trait.type == 'delegate') and (name[-6:] != '_items'): self._init_trait_delegate_listener( name, 'delegate', get_delegate_pattern( name, trait ) ) #--------------------------------------------------------------------------- # Adds/Removes a trait instance creation monitor: #--------------------------------------------------------------------------- def trait_monitor ( cls, handler, remove = False ): """Adds or removes the specified *handler* from the list of active monitors. Parameters ---------- handler : function The function to add or remove as a monitor. remove : bool Flag indicating whether to remove (True) or add the specified handler as a monitor for this class. Description ----------- If *remove* is omitted or False, the specified handler is added to the list of active monitors; if *remove* is True, the handler is removed from the active monitor list. """ global _HasTraits_monitors index = _trait_monitor_index( cls, handler ) if remove: if index >= 0: del _HasTraits_monitors[ index ] return if index < 0: _HasTraits_monitors.append( ( cls, handler ) ) trait_monitor = classmethod( trait_monitor ) #--------------------------------------------------------------------------- # Add a new class trait (i.e. applies to all instances and subclasses): #--------------------------------------------------------------------------- def add_class_trait ( cls, name, *trait ): """ Adds a named trait attribute to this class. Parameters ---------- name : str Name of the attribute to add. *trait : A trait or a value that can be converted to a trait using Trait() Trait definition of the attribute. It can be a single value or a list equivalent to an argument list for the Trait() function. """ # Make sure a trait argument was specified: if len( trait ) == 0: raise ValueError, 'No trait definition was specified.' # Make sure only valid traits get added: if len( trait ) > 1: trait = Trait( *trait ) else: trait = _trait_for( trait[0] ) # Add the trait to the class: cls._add_class_trait( name, trait, False ) # Also add the trait to all subclasses of this class: for subclass in cls.trait_subclasses( True ): subclass._add_class_trait( name, trait, True ) add_class_trait = classmethod( add_class_trait ) def _add_class_trait ( cls, name, trait, is_subclass ): # Get a reference to the class's dictionary and 'prefix' traits: class_dict = cls.__dict__ prefix_traits = class_dict[ PrefixTraits ] # See if the trait is a 'prefix' trait: if name[-1:] == '_': name = name[:-1] if name in prefix_traits: if is_subclass: return raise TraitError( "The '%s_' trait is already defined." % name ) prefix_traits[ name ] = trait # Otherwise, add it to the list of known prefixes: prefix_list = prefix_traits['*'] prefix_list.append( name ) # Resort the list from longest to shortest: prefix_list.sort( lambda x, y: len( y ) - len( x ) ) return # Check to see if the trait is already defined: class_traits = class_dict[ ClassTraits ] if class_traits.get( name ) is not None: if is_subclass: return raise TraitError( "The '%s' trait is already defined." % name ) # Check to see if the trait has additional sub-traits that need to be # defined also: handler = trait.handler if handler is not None: if handler.has_items: cls.add_class_trait( name + '_items', handler.items_event() ) if handler.is_mapped: cls.add_class_trait( name + '_', _mapped_trait_for( trait ) ) # Make the new trait inheritable (if allowed): if trait.is_base is not False: class_dict[ BaseTraits ][ name ] = trait # See if there are any static notifiers defined: handlers = [ _get_method( cls, '_%s_changed' % name ), _get_method( cls, '_%s_fired' % name ) ] # Add any special trait defined event handlers: _add_event_handlers( trait, cls, handlers ) # Add the 'anytrait' handler (if any): handlers.append( prefix_traits.get( '@' ) ) # Filter out any 'None' values: handlers = [ h for h in handlers if h is not None ] # If there are and handlers, add them to the trait's notifier's list: if len( handlers ) > 0: trait = _clone_trait( trait ) _add_notifiers( trait._notifiers( 1 ), handlers ) # Finally, add the new trait to the class trait dictionary: class_traits[ name ] = trait _add_class_trait = classmethod( _add_class_trait ) #--------------------------------------------------------------------------- # Adds a 'category' to the class: #--------------------------------------------------------------------------- def add_trait_category ( cls, category ): """ Adds a trait category to a class. """ if issubclass( category, HasTraits ): cls._add_trait_category( getattr( category, BaseTraits ), getattr( category, ClassTraits ), getattr( category, InstanceTraits ), getattr( category, PrefixTraits ), getattr( category, ListenerTraits ), getattr( category, ViewTraits, None ) ) # Copy all methods that are not already in the class from the category: for subcls in category.__mro__: for name, value in subcls.__dict__.items(): if not hasattr( cls, name ): setattr( cls, name, value ) add_trait_category = classmethod( add_trait_category ) #--------------------------------------------------------------------------- # Adds a 'category' to the class: #--------------------------------------------------------------------------- def _add_trait_category ( cls, base_traits, class_traits, instance_traits, prefix_traits, listeners, view_elements ): # Update the class and each of the existing subclasses: for subclass in [ cls ] + cls.trait_subclasses( True ): # Merge the 'base_traits': subclass_traits = getattr( subclass, BaseTraits ) for name, value in base_traits.items(): subclass_traits.setdefault( name, value ) # Merge the 'class_traits': subclass_traits = getattr( subclass, ClassTraits ) for name, value in class_traits.items(): subclass_traits.setdefault( name, value ) # Merge the 'instance_traits': subclass_traits = getattr( subclass, InstanceTraits ) for name, arg_lists in instance_traits.items(): subclass_arg_lists = subclass_traits.get( name ) if subclass_arg_lists is None: subclass_traits[ name ] = arg_lists[:] else: for arg_list in arg_lists: if arg_list not in subclass_arg_lists: subclass_arg_lists.append( arg_list ) # Merge the 'prefix_traits': subclass_traits = getattr( subclass, PrefixTraits ) subclass_list = subclass_traits['*'] changed = False for name, value in prefix_traits.items(): if name not in subclass_traits: subclass_traits[ name ] = value subclass_list.append( name ) changed = True # Resort the list from longest to shortest (if necessary): if changed: subclass_list.sort( key = lambda x: -len( x ) ) # Merge the 'listeners': subclass_traits = getattr( subclass, ListenerTraits ) for name, value in listeners.items(): subclass_traits.setdefault( name, value ) # Copy all our new view elements into the base class's ViewElements: if view_elements is not None: content = view_elements.content if len( content ) > 0: base_ve = getattr( cls, ViewTraits, None ) if base_ve is None: base_ve = ViewElements() setattr( cls, ViewTraits, base_ve ) base_ve_content = base_ve.content for name, value in content.items(): base_ve_content.setdefault( name, value ) _add_trait_category = classmethod( _add_trait_category ) #--------------------------------------------------------------------------- # Sets a trait notification dispatch handler: #--------------------------------------------------------------------------- def set_trait_dispatch_handler ( cls, name, klass, override = False ): """ Sets a trait notification dispatch handler. """ try: if issubclass( klass, TraitChangeNotifyWrapper ): if (not override) and (name in cls.wrappers): raise TraitError, ("A dispatch handler called '%s' has " "already been defined." % name) cls.wrappers[ name ] = klass return except TypeError: pass raise TraitError, ('%s is not a subclass of TraitChangeNotifyWrapper.' % klass) set_trait_dispatch_handler = classmethod( set_trait_dispatch_handler ) #--------------------------------------------------------------------------- # Returns the immediate (or all) subclasses of this class: #--------------------------------------------------------------------------- def trait_subclasses ( cls, all = False ): """ Returns a list of the immediate (or all) subclasses of this class. Parameters ---------- all : bool Indicates whether to return all subclasses of this class. If False, only immediate subclasses are returned. """ if not all: return cls.__subclasses__() return cls._trait_subclasses( [] ) trait_subclasses = classmethod( trait_subclasses ) def _trait_subclasses ( cls, subclasses ): for subclass in cls.__subclasses__(): if subclass not in subclasses: subclasses.append( subclass ) subclass._trait_subclasses( subclasses ) return subclasses _trait_subclasses = classmethod( _trait_subclasses ) #--------------------------------------------------------------------------- # Returns whether the object implements a specified traits interface: #--------------------------------------------------------------------------- def has_traits_interface ( self, *interfaces ): """Returns whether the object implements a specified traits interface. Parameters ---------- *interfaces : One or more traits Interface (sub)classes. Description ----------- Tests whether the object implements one or more of the interfaces specified by *interfaces*. Return **True** if it does, and **False** otherwise. """ return isinstance(self, interfaces) #--------------------------------------------------------------------------- # Prepares an object to be pickled: #--------------------------------------------------------------------------- def __getstate__ ( self ): """ Returns a dictionary of traits to pickle. In general, avoid overriding __getstate__ in subclasses. Instead, mark traits that should not be pickled with 'transient = True' metadata. In cases where this strategy is not sufficient, override __getstate__ in subclasses using the following pattern to remove items that should not be persisted:: def __getstate__(self): state = super(X,self).__getstate__() for key in ['foo', 'bar']: if key in state: del state[key] return state """ # Save all traits which do not have any 'transient' metadata: result = self.trait_get( transient = is_none ) # Add all delegate traits that explicitly have 'transient = False' # metadata: dic = self.__dict__ result.update( dict( [ ( name, dic[ name ] ) for name in self.trait_names( type = 'delegate', transient = False ) if name in dic ] ) ) # If this object implements ISerializable, make sure that all # contained HasTraits objects in its persisted state also implement # ISerializable: if self.has_traits_interface( ISerializable ): for name, value in result.items(): if not _is_serializable( value ): raise TraitError( "The '%s' trait of a '%s' instance " "contains the unserializable value: %s" % ( name, self.__class__.__name__, value ) ) # Store the traits version in the state dictionary (if possible): result.setdefault( '__traits_version__', TraitsVersion ) # Return the final state dictionary: return result def __reduce_ex__ ( self, protocol ): return ( __newobj__, ( self.__class__, ), self.__getstate__() ) #--------------------------------------------------------------------------- # Restores the previously pickled state of an object: #--------------------------------------------------------------------------- def __setstate__ ( self, state, trait_change_notify = True ): """ Restores the previously pickled state of an object. """ pop = state.pop if pop( '__traits_version__', None ) is None: # If the state was saved by a version of Traits prior to 3.0, then # use Traits 2.0 compatible code to restore it: values = [ ( name, pop( name ) ) for name in pop( '__HasTraits_restore__', [] ) ] self.__dict__.update( state ) self.trait_set( trait_change_notify=trait_change_notify, **dict( values ) ) else: # Otherwise, apply the Traits 3.0 restore logic: self._init_trait_listeners() self.trait_set( trait_change_notify = trait_change_notify, **state ) self._post_init_trait_listeners() self.traits_init() self.traits_inited( True ) #--------------------------------------------------------------------------- # Shortcut for retrieving the value of a list of traits: #--------------------------------------------------------------------------- def trait_get ( self, *names, **metadata ): """ Shortcut for getting object trait attributes. Parameters ---------- names : list of strings A list of trait attribute names whose values are requested. Returns ------- result : dict A dictionary whose keys are the names passed as arguments and whose values are the corresponding trait values. Description ----------- Looks up the value of each trait whose name is passed as an argument and returns a dictionary containing the resulting name/value pairs. If any name does not correspond to a defined trait, it is not included in the result. If no names are specified, the result is a dictionary containing name/value pairs for *all* traits defined on the object. """ result = {} n = len( names ) if (n == 1) and (type( names[0] ) in SequenceTypes): names = names[0] elif n == 0: names = self.trait_names( **metadata ) for name in names: value = getattr( self, name, Missing ) if value is not Missing: result[ name ] = value return result # Defines the deprecated alias for 'trait_get' @deprecated('use "HasTraits.trait_get" instead') def get( self, *names, **metadata ): return self.trait_get( *names, **metadata ) get.__doc__ = trait_get.__doc__ #--------------------------------------------------------------------------- # Shortcut for setting object traits: #--------------------------------------------------------------------------- def trait_set ( self, trait_change_notify = True, **traits ): """ Shortcut for setting object trait attributes. Parameters ---------- trait_change_notify : bool If **True** (the default), then each value assigned may generate a trait change notification. If **False**, then no trait change notifications will be generated. (see also: trait_setq) **traits : Key/value pairs, the trait attributes and their values to be set Returns ------- self : The method returns this object, after setting attributes. Description ----------- Treats each keyword argument to the method as the name of a trait attribute and sets the corresponding trait attribute to the value specified. This is a useful shorthand when a number of trait attributes need to be set on an object, or a trait attribute value needs to be set in a lambda function. For example, you can write:: person.trait_set(name='Bill', age=27) instead of:: person.name = 'Bill' person.age = 27 """ if not trait_change_notify: self._trait_change_notify( False ) try: for name, value in traits.items(): setattr( self, name, value ) finally: self._trait_change_notify( True ) else: for name, value in traits.items(): setattr( self, name, value ) return self # Defines the deprecated alias for 'trait_set' @deprecated('use "HasTraits.trait_set" instead') def set ( self, trait_change_notify = True, **traits ): return self.trait_set( trait_change_notify=trait_change_notify, **traits) set.__doc__ = trait_set.__doc__ def trait_setq ( self, **traits ): """ Shortcut for setting object trait attributes. Parameters ---------- **traits : Key/value pairs, the trait attributes and their values to be set. No trait change notifications will be generated for any values assigned (see also: trait_set). Returns ------- self : The method returns this object, after setting attributes. Description ----------- Treats each keyword argument to the method as the name of a trait attribute and sets the corresponding trait attribute to the value specified. This is a useful shorthand when a number of trait attributes need to be set on an object, or a trait attribute value needs to be set in a lambda function. For example, you can write:: person.trait_setq(name='Bill', age=27) instead of:: person.name = 'Bill' person.age = 27 """ return self.trait_set( trait_change_notify = False, **traits ) #--------------------------------------------------------------------------- # Resets some or all of an object's traits to their default values: #--------------------------------------------------------------------------- def reset_traits ( self, traits = None, **metadata ): """ Resets some or all of an object's trait attributes to their default values. Parameters ---------- traits : list of strings Names of trait attributes to reset. Returns ------- unresetable : list of strings A list of attributes that the method was unable to reset, which is empty if all the attributes were successfully reset. Description ----------- Resets each of the traits whose names are specified in the *traits* list to their default values. If *traits* is None or omitted, the method resets all explicitly-defined object trait attributes to their default values. Note that this does not affect wildcard trait attributes or trait attributes added via add_trait(), unless they are explicitly named in *traits*. """ unresetable = [] if traits is None: traits = self.trait_names( **metadata ) for name in traits: try: delattr( self, name ) except ( AttributeError, TraitError ): unresetable.append( name ) return unresetable #--------------------------------------------------------------------------- # Returns the list of trait names to copy/clone by default: #--------------------------------------------------------------------------- def copyable_trait_names ( self, **metadata ): """ Returns the list of trait names to copy or clone by default. """ metadata.setdefault('transient', lambda t: t is not True) return self.trait_names( **metadata ) #--------------------------------------------------------------------------- # Returns the list of all trait names, including implicitly defined # traits: #--------------------------------------------------------------------------- def all_trait_names ( self ): """ Returns the list of all trait names, including implicitly defined traits. """ return self.__class_traits__.keys() #--------------------------------------------------------------------------- # Copies another object's traits into this one: #--------------------------------------------------------------------------- def copy_traits ( self, other, traits = None, memo = None, copy = None, **metadata ): """ Copies another object's trait attributes into this one. Parameters ---------- other : object The object whose trait attribute values should be copied. traits : list of strings A list of names of trait attributes to copy. If None or unspecified, the set of names returned by trait_names() is used. If 'all' or an empty list, the set of names returned by all_trait_names() is used. memo : dict A dictionary of objects that have already been copied. copy : None | 'deep' | 'shallow' The type of copy to perform on any trait that does not have explicit 'copy' metadata. A value of None means 'copy reference'. Returns ------- unassignable : list of strings A list of attributes that the method was unable to copy, which is empty if all the attributes were successfully copied. """ if traits is None: traits = self.copyable_trait_names( **metadata ) elif (traits == 'all') or (len( traits ) == 0): traits = self.all_trait_names() if memo is not None: memo[ 'traits_to_copy' ] = 'all' unassignable = [] deferred = [] deep_copy = (copy == 'deep') shallow_copy = (copy == 'shallow') for name in traits: try: trait = self.trait( name ) if trait.type in DeferredCopy: deferred.append( name ) continue base_trait = other.base_trait( name ) if base_trait.type == 'event': continue value = getattr( other, name ) copy_type = base_trait.copy if copy_type == 'shallow': value = copy_module.copy( value ) elif copy_type == 'ref': pass elif (copy_type == 'deep') or deep_copy: if memo is None: value = copy_module.deepcopy( value ) else: value = copy_module.deepcopy( value, memo ) elif shallow_copy: value = copy_module.copy( value ) setattr( self, name, value ) except: unassignable.append( name ) for name in deferred: try: value = getattr( other, name ) copy_type = other.base_trait( name ).copy if copy_type == 'shallow': value = copy_module.copy( value ) elif copy_type == 'ref': pass elif (copy_type == 'deep') or deep_copy: if memo is None: value = copy_module.deepcopy( value ) else: value = copy_module.deepcopy( value, memo ) elif shallow_copy: value = copy_module.copy( value ) setattr( self, name, value ) except: unassignable.append( name ) return unassignable #--------------------------------------------------------------------------- # Clones a new object from this one, optionally copying only a specified # set of traits: #--------------------------------------------------------------------------- def clone_traits ( self, traits = None, memo = None, copy = None, **metadata ): """ Clones a new object from this one, optionally copying only a specified set of traits. Parameters ---------- traits : list of strings The list of names of the trait attributes to copy. memo : dict A dictionary of objects that have already been copied. copy : str The type of copy ``deep`` or ``shallow`` to perform on any trait that does not have explicit 'copy' metadata. A value of None means 'copy reference'. Returns ------- new : The newly cloned object. Description ----------- Creates a new object that is a clone of the current object. If *traits* is None (the default), then all explicit trait attributes defined for this object are cloned. If *traits* is 'all' or an empty list, the list of traits returned by all_trait_names() is used; otherwise, *traits* must be a list of the names of the trait attributes to be cloned. """ if memo is None: memo = {} if traits is None: traits = self.copyable_trait_names( **metadata ) elif (traits == 'all') or (len( traits ) == 0): traits = self.all_trait_names() memo[ 'traits_to_copy' ] = 'all' memo[ 'traits_copy_mode' ] = copy new = self.__new__( self.__class__ ) memo[ id( self ) ] = new new._init_trait_listeners() new.copy_traits( self, traits, memo, copy, **metadata ) new._post_init_trait_listeners() new.traits_init() new.traits_inited( True ) return new #--------------------------------------------------------------------------- # Creates a deep copy of the object: #--------------------------------------------------------------------------- def __deepcopy__ ( self, memo ): """ Creates a deep copy of the object. """ id_self = id( self ) if id_self in memo: return memo[ id_self ] result = self.clone_traits( memo = memo, traits = memo.get( 'traits_to_copy' ), copy = memo.get( 'traits_copy_mode' ) ) return result #--------------------------------------------------------------------------- # Edits the object's traits: #--------------------------------------------------------------------------- def edit_traits ( self, view = None, parent = None, kind = None, context = None, handler = None, id = '', scrollable = None, **args ): """ Displays a user interface window for editing trait attribute values. Parameters ---------- view : View or string A View object (or its name) that defines a user interface for editing trait attribute values of the current object. If the view is defined as an attribute on this class, use the name of the attribute. Otherwise, use a reference to the view object. If this attribute is not specified, the View object returned by trait_view() is used. parent : toolkit control The reference to a user interface component to use as the parent window for the object's UI window. kind : str The type of user interface window to create. See the **traitsui.view.kind_trait** trait for values and their meanings. If *kind* is unspecified or None, the **kind** attribute of the View object is used. context : object or dictionary A single object or a dictionary of string/object pairs, whose trait attributes are to be edited. If not specified, the current object is used. handler : Handler A handler object used for event handling in the dialog box. If None, the default handler for Traits UI is used. id : str A unique ID for persisting preferences about this user interface, such as size and position. If not specified, no user preferences are saved. scrollable : bool Indicates whether the dialog box should be scrollable. When set to True, scroll bars appear on the dialog box if it is not large enough to display all of the items in the view at one time. Returns ------- A UI object. """ if context is None: context = self view = self.trait_view( view ) return view.ui( context, parent, kind, self.trait_view_elements(), handler, id, scrollable, args ) #--------------------------------------------------------------------------- # Returns the default context to use for editing/configuring traits: #--------------------------------------------------------------------------- def trait_context ( self ): """ Returns the default context to use for editing or configuring traits. """ return { 'object': self } #--------------------------------------------------------------------------- # Gets or sets a ViewElement associated with an object's class: #--------------------------------------------------------------------------- def trait_view ( self, name = None, view_element = None ): """ Gets or sets a ViewElement associated with an object's class. Parameters ---------- name : str Name of a view element view_element : ViewElement View element to associate Returns ------- A view element. Description ----------- If both *name* and *view_element* are specified, the view element is associated with *name* for the current object's class. (That is, *view_element* is added to the ViewElements object associated with the current object's class, indexed by *name*.) If only *name* is specified, the function returns the view element object associated with *name*, or None if *name* has no associated view element. View elements retrieved by this function are those that are bound to a class attribute in the class definition, or that are associated with a name by a previous call to this method. If neither *name* nor *view_element* is specified, the method returns a View object, based on the following order of preference: 1. If there is a View object named ``traits_view`` associated with the current object, it is returned. 2. If there is exactly one View object associated the current object, it is returned. 3. Otherwise, it returns a View object containing items for all the non-event trait attributes on the current object. """ return self.__class__._trait_view( name, view_element, self.default_traits_view, self.trait_view_elements, self.visible_traits, self ) def class_trait_view ( cls, name = None, view_element = None ): return cls._trait_view( name, view_element, cls.class_default_traits_view, cls.class_trait_view_elements, cls.class_visible_traits, None ) class_trait_view = classmethod( class_trait_view ) #--------------------------------------------------------------------------- # Gets or sets a ViewElement associated with an object's class: #--------------------------------------------------------------------------- def _trait_view ( cls, name, view_element, default_name, view_elements, trait_selector_f, handler ): """ Gets or sets a ViewElement associated with an object's class. """ # If a view element was passed instead of a name or None, return it: if isinstance( name, ViewElement ): return name # Get the ViewElements object associated with the class: view_elements = view_elements() # The following test should only succeed for objects created before # traits has been fully initialized (such as the default Handler): if view_elements is None: return None if name: if view_element is None: # If only a name was specified, return the ViewElement it # matches, if any: result = view_elements.find( name ) if (result is None) and (handler is not None): method = getattr( handler, name, None ) if callable( method ): result = method() return result # Otherwise, save the specified ViewElement under the name # specified: view_elements.content[ name ] = view_element return None # Get the default view/view name: name = default_name() # If the default is a View, return it: if isinstance( name, ViewElement ): return name # Otherwise, get all View objects associated with the object's class: names = view_elements.filter_by() # If the specified default name is in the list, return its View: if name in names: return view_elements.find( name ) if handler is not None: method = getattr( handler, name, None ) if callable( method ): result = method() if isinstance( result, ViewElement ): return result # If there is only one View, return it: if len( names ) == 1: return view_elements.find( names[0] ) # Otherwise, create and return a View based on the set of editable # traits defined for the object: from traitsui.api import View return View( trait_selector_f(), buttons = [ 'OK', 'Cancel' ] ) _trait_view = classmethod( _trait_view ) #--------------------------------------------------------------------------- # Return the default traits view/name: #--------------------------------------------------------------------------- def default_traits_view ( self ): """ Returns the name of the default traits view for the object's class. """ return self.__class__.class_default_traits_view() #--------------------------------------------------------------------------- # Return the default traits view/name: #--------------------------------------------------------------------------- def class_default_traits_view ( cls ): """ Returns the name of the default traits view for the class. """ return DefaultTraitsView class_default_traits_view = classmethod( class_default_traits_view ) #--------------------------------------------------------------------------- # Gets the list of names of ViewElements associated with the object's # class that are of a specified ViewElement type: #--------------------------------------------------------------------------- def trait_views ( self, klass = None ): """ Returns a list of the names of all view elements associated with the current object's class. Parameters ---------- klass : class A class, such that all returned names must correspond to instances of this class. Possible values include: * Group * Item * View * ViewElement * ViewSubElement Description ----------- If *klass* is specified, the list of names is filtered such that only objects that are instances of the specified class are returned. """ return self.__class__.__dict__[ ViewTraits ].filter_by( klass ) #--------------------------------------------------------------------------- # Returns the ViewElements object associated with the object's class: #--------------------------------------------------------------------------- def trait_view_elements ( self ): """ Returns the ViewElements object associated with the object's class. The returned object can be used to access all the view elements associated with the class. """ return self.__class__.class_trait_view_elements() def class_trait_view_elements ( cls ): """ Returns the ViewElements object associated with the class. The returned object can be used to access all the view elements associated with the class. """ return cls.__dict__[ ViewTraits ] class_trait_view_elements = classmethod( class_trait_view_elements ) #--------------------------------------------------------------------------- # Configure the object's traits: #--------------------------------------------------------------------------- def configure_traits ( self, filename = None, view = None, kind = None, edit = True, context = None, handler = None, id = '', scrollable = None, **args ): ### JMS: Is it correct to assume that non-modal options for 'kind' ### behave modally when called from this method? """Creates and displays a dialog box for editing values of trait attributes, as if it were a complete, self-contained GUI application. Parameters ---------- filename : str The name (including path) of a file that contains a pickled representation of the current object. When this parameter is specified, the method reads the corresponding file (if it exists) to restore the saved values of the object's traits before displaying them. If the user confirms the dialog box (by clicking **OK**), the new values are written to the file. If this parameter is not specified, the values are loaded from the in-memory object, and are not persisted when the dialog box is closed. view : View or str A View object (or its name) that defines a user interface for editing trait attribute values of the current object. If the view is defined as an attribute on this class, use the name of the attribute. Otherwise, use a reference to the view object. If this attribute is not specified, the View object returned by trait_view() is used. kind : str The type of user interface window to create. See the **traitsui.view.kind_trait** trait for values and their meanings. If *kind* is unspecified or None, the **kind** attribute of the View object is used. edit : bool Indicates whether to display a user interface. If *filename* specifies an existing file, setting *edit* to False loads the saved values from that file into the object without requiring user interaction. context : object or dictionary A single object or a dictionary of string/object pairs, whose trait attributes are to be edited. If not specified, the current object is used handler : Handler A handler object used for event handling in the dialog box. If None, the default handler for Traits UI is used. id : str A unique ID for persisting preferences about this user interface, such as size and position. If not specified, no user preferences are saved. scrollable : bool Indicates whether the dialog box should be scrollable. When set to True, scroll bars appear on the dialog box if it is not large enough to display all of the items in the view at one time. Returns ------- True on success. Description ----------- This method is intended for use in applications that do not normally have a GUI. Control does not resume in the calling application until the user closes the dialog box. The method attempts to open and unpickle the contents of *filename* before displaying the dialog box. When editing is complete, the method attempts to pickle the updated contents of the object back to *filename*. If the file referenced by *filename* does not exist, the object is not modified before displaying the dialog box. If *filename* is unspecified or None, no pickling or unpickling occurs. If *edit* is True (the default), a dialog box for editing the current object is displayed. If *edit* is False or None, no dialog box is displayed. You can use ``edit=False`` if you want the object to be restored from the contents of *filename*, without being modified by the user. """ if filename is not None: fd = None try: import cPickle fd = open( filename, 'rb' ) self.copy_traits( cPickle.Unpickler( fd ).load() ) except: if fd is not None: fd.close() if edit: from traitsui.api import toolkit if context is None: context = self rc = toolkit().view_application( context, self.trait_view( view ), kind, handler, id, scrollable, args ) if rc and (filename is not None): fd = None try: import cPickle fd = open( filename, 'wb' ) cPickle.Pickler( fd, True ).dump( self ) finally: if fd is not None: fd.close() return rc return True #--------------------------------------------------------------------------- # Return the list of editable traits: #--------------------------------------------------------------------------- def editable_traits ( self ): """Returns an alphabetically sorted list of the names of non-event trait attributes associated with the current object. """ names = self.trait_names( type = not_event, editable = not_false ) names.sort() return names def class_editable_traits ( cls ): """Returns an alphabetically sorted list of the names of non-event trait attributes associated with the current class. """ names = cls.class_trait_names( type = not_event, editable = not_false ) names.sort() return names class_editable_traits = classmethod( class_editable_traits ) def visible_traits ( self ): """Returns an alphabetically sorted list of the names of non-event trait attributes associated with the current object, that should be GUI visible """ return self.trait_names( type = not_event, visible = not_false ) def class_visible_traits ( cls ): """Returns an alphabetically sorted list of the names of non-event trait attributes associated with the current class, that should be GUI visible """ return cls.class_trait_names( type = not_event, visible = not_false ) class_visible_traits = classmethod( class_visible_traits ) #--------------------------------------------------------------------------- # Pretty print the traits of an object: #--------------------------------------------------------------------------- def print_traits ( self, show_help = False, **metadata ): """Prints the values of all explicitly-defined, non-event trait attributes on the current object, in an easily readable format. Parameters ---------- show_help : bool Indicates whether to display additional descriptive information. """ if len( metadata ) > 0: names = self.trait_names( **metadata ) else: names = self.trait_names( type = not_event ) if len( names ) == 0: print '' return result = [] pad = max( [ len( x ) for x in names ] ) + 1 maxval = 78 - pad names.sort() for name in names: try: value = repr( getattr( self, name ) ).replace( '\n', '\\n' ) if len( value ) > maxval: value = '%s...%s' % ( value[: (maxval - 2) // 2 ], value[ -((maxval - 3) // 2): ] ) except: value = '' lname = (name + ':').ljust( pad ) if show_help: result.append( '%s %s\n The value must be %s.' % ( lname, value, self.base_trait( name ).setter.info() ) ) else: result.append( '%s %s' % ( lname, value ) ) print '\n'.join( result ) #--------------------------------------------------------------------------- # Add/Remove a handler for a specified trait being changed: # # If no name is specified, the handler will be invoked for any trait # change. #--------------------------------------------------------------------------- def _on_trait_change ( self, handler, name = None, remove = False, dispatch = 'same', priority = False, target = None): """Causes the object to invoke a handler whenever a trait attribute is modified, or removes the association. Parameters ---------- handler : function A trait notification function for the attribute specified by *name*. name : str Specifies the trait attribute whose value changes trigger the notification. remove : bool If True, removes the previously-set association between *handler* and *name*; if False (the default), creates the association. Description ----------- Multiple handlers can be defined for the same object, or even for the same trait attribute on the same object. If *name* is not specified or is None, *handler* is invoked when any trait attribute on the object is changed. """ if type( name ) is list: for name_i in name: self._on_trait_change( handler, name_i, remove, dispatch, priority, target ) return name = name or 'anytrait' if remove: if name == 'anytrait': notifiers = self._notifiers( 0 ) else: trait = self._trait( name, 1 ) if trait is None: return notifiers = trait._notifiers( 0 ) if notifiers is not None: for i, notifier in enumerate( notifiers ): if notifier.equals( handler ): del notifiers[i] notifier.dispose() break return if name == 'anytrait': notifiers = self._notifiers( 1 ) else: notifiers = self._trait( name, 2 )._notifiers( 1 ) for notifier in notifiers: if notifier.equals( handler ): break else: wrapper = self.wrappers[ dispatch ]( handler, notifiers, target ) if priority: notifiers.insert( 0, wrapper ) else: notifiers.append( wrapper ) #--------------------------------------------------------------------------- # Add/Remove handlers for an extended set of one or more traits being # changed: # # If no name is specified, the handler will be invoked for any trait # change. #--------------------------------------------------------------------------- def on_trait_change ( self, handler, name = None, remove = False, dispatch = 'same', priority = False, deferred = False, target = None ): """Causes the object to invoke a handler whenever a trait attribute matching a specified pattern is modified, or removes the association. Parameters ---------- handler : function A trait notification function for the *name* trait attribute, with one of the signatures described below. name : str The name of the trait attribute whose value changes trigger the notification. The *name* can specify complex patterns of trait changes using an extended *name* syntax, which is described below. remove : bool If True, removes the previously-set association between *handler* and *name*; if False (the default), creates the association. dispatch : str A string indicating the thread on which notifications must be run. Possible values are: =========== ======================================================= value dispatch =========== ======================================================= ``same`` Run notifications on the same thread as this one. ``ui`` Run notifications on the UI thread. If the current thread is the UI thread, the notifications are executed immediately; otherwise, they are placed on the UI event queue. ``fast_ui`` Alias for ``ui``. ``new`` Run notifications in a new thread. =========== ======================================================= Description ----------- Multiple handlers can be defined for the same object, or even for the same trait attribute on the same object. If *name* is not specified or is None, *handler* is invoked when any trait attribute on the object is changed. The *name* parameter is a single *xname* or a list of *xname* names, where an *xname* is an extended name of the form:: xname2[('.'|':') xname2]* An *xname2* is of the form:: ( xname3 | '['xname3[','xname3]*']' ) ['*'] An *xname3* is of the form:: xname | ['+'|'-'][name] | name['?' | ('+'|'-')[name]] A *name* is any valid Python attribute name. The semantic meaning of this notation is as follows: ================================ ====================================== expression meaning ================================ ====================================== ``item1.item2`` means *item1* is a trait containing an object (or objects if *item1* is a list or dict) with a trait called *item2*. Changes to either *item1* or *item2* cause a notification to be generated. ``item1:item2`` means *item1* is a trait containing an object (or objects if *item1* is a list or dict) with a trait called *item2*. Changes to *item2* cause a notification to be generated, while changes to *item1* do not (i.e., the ':' indicates that changes to the *link* object should not be reported). ``[ item1, item2, ..., itemN ]`` A list which matches any of the specified items. Note that at the topmost level, the surrounding square brackets are optional. ``name?`` If the current object does not have an attribute called *name*, the reference can be ignored. If the '?' character is omitted, the current object must have a trait called *name*, otherwise an exception will be raised. ``prefix+`` Matches any trait on the object whose name begins with *prefix*. ``+metadata_name`` Matches any trait on the object having *metadata_name* metadata. ``-metadata_name`` Matches any trait on the object which does not have *metadata_name* metadata. ``prefix+metadata_name`` Matches any trait on the object whose name begins with *prefix* and which has *metadata_name* metadata. ``prefix-metadata_name`` Matches any trait on the object whose name begins with *prefix* and which does not have *metadata_name* metadata. ``+`` Matches all traits on the object. ``pattern*`` Matches object graphs where *pattern* occurs one or more times (useful for setting up listeners on recursive data structures like trees or linked lists). ================================ ====================================== Some examples of valid names and their meaning are as follows: ======================= =============================================== example meaning ======================= =============================================== ``foo,bar,baz`` Listen for trait changes to *object.foo*, *object.bar*, and *object.baz*. ``['foo','bar','baz']`` Equivalent to 'foo,bar,baz', but may be more useful in cases where the individual items are computed. ``foo.bar.baz`` Listen for trait changes to *object.foo.bar.baz* and report changes to *object.foo*, *object.foo.bar* or *object.foo.bar.baz*. ``foo:bar:baz`` Listen for changes to *object.foo.bar.baz*, and only report changes to *object.foo.bar.baz*. ``foo.[bar,baz]`` Listen for trait changes to *object.foo.bar* and *object.foo.baz*. ``[left,right]*.name`` Listen for trait changes to the *name* trait of each node of a tree having *left* and *right* links to other tree nodes, and where *object* the method is applied to the root node of the tree. ``+dirty`` Listen for trait changes on any trait in the *object* which has the 'dirty' metadata set. ``foo.+dirty`` Listen for trait changes on any trait in *object.foo* which has the 'dirty' metadata set. ``foo.[bar,-dirty]`` Listen for trait changes on *object.foo.bar* or any trait on *object.foo* which does not have 'dirty' metadata set. ======================= =============================================== Note that any of the intermediate (i.e., non-final) links in a pattern can be traits of type Instance, List or Dict. In the case of List and Dict traits, the subsequent portion of the pattern is applied to each item in the list, or value in the dictionary. For example, if the self.children is a list, 'children.name' listens for trait changes to the *name* trait for each item in the self.children list. Note that items added to or removed from a list or dictionary in the pattern will cause the *handler* routine to be invoked as well, since this is treated as an *implied* change to the item's trait being monitored. The signature of the *handler* supplied also has an effect on how changes to intermediate traits are processed. The five valid handler signatures are: 1. handler() 2. handler(new) 3. handler(name,new) 4. handler(object,name,new) 5. handler(object,name,old,new) For signatures 1, 4 and 5, any change to any element of a path being listened to invokes the handler with information about the particular element that was modified (e.g., if the item being monitored is 'foo.bar.baz', a change to 'bar' will call *handler* with the following information: - object: object.foo - name: bar - old: old value for object.foo.bar - new: new value for object.foo.bar If one of the intermediate links is a List or Dict, the call to *handler* may report an *_items* changed event. If in the previous example, *bar* is a List, and a new item is added to *bar*, then the information passed to *handler* would be: - object: object.foo - name: bar_items - old: Undefined - new: TraitListEvent whose *added* trait contains the new item added to *bar*. For signatures 2 and 3, the *handler* does not receive enough information to discern between a change to the final trait being listened to and a change to an intermediate link. In this case, the event dispatcher will attempt to map a change to an intermediate link to its effective change on the final trait. This only works if all of the intermediate links are single values (such as an Instance or Any trait) and not Lists or Dicts. If the modified intermediate trait or any subsequent intermediate trait preceding the final trait is a List or Dict, then a TraitError is raised, since the effective value for the final trait cannot in general be resolved unambiguously. To prevent TraitErrors in this case, use the ':' separator to suppress notifications for changes to any of the intermediate links. Handler signature 1 also has the special characteristic that if a final trait is a List or Dict, it will automatically handle '_items' changed events for the final trait as well. This can be useful in cases where the *handler* only needs to know that some aspect of the final trait has been changed. For all other *handler* signatures, you must explicitly specify the 'xxx_items' trait if you want to be notified of changes to any of the items of the 'xxx' trait. """ # Check to see if we can do a quick exit to the basic trait change # handler: if ((isinstance( name, basestring ) and (extended_trait_pat.match( name ) is None)) or (name is None)): self._on_trait_change( handler, name, remove, dispatch, priority, target ) return from .traits_listener \ import TraitsListener, ListenerParser, ListenerHandler, \ ListenerNotifyWrapper if isinstance( name, list ): for name_i in name: self.on_trait_change( handler, name_i, remove, dispatch, priority, target ) return # Make sure we have a name string: name = (name or 'anytrait').strip() if remove: dict = self.__dict__.get( TraitsListener ) if dict is not None: listeners = dict.get( name ) if listeners is not None: for i, wrapper in enumerate( listeners ): if wrapper.equals( handler ): del listeners[i] if len( listeners ) == 0: del dict[ name ] if len( dict ) == 0: del self.__dict__[ TraitsListener ] wrapper.listener.unregister( self ) wrapper.dispose() break else: dict = self.__dict__.setdefault( TraitsListener, {} ) listeners = dict.setdefault( name, [] ) for wrapper in listeners: if wrapper.equals( handler ): break else: listener = ListenerParser( name ).listener lnw = ListenerNotifyWrapper( handler, self, name, listener, target ) listeners.append( lnw ) listener.trait_set( handler = ListenerHandler( handler ), wrapped_handler_ref = weakref.ref(lnw), type = lnw.type, dispatch = dispatch, priority = priority, deferred = deferred ) listener.register( self ) # A synonym for 'on_trait_change' on_trait_event = on_trait_change #--------------------------------------------------------------------------- # Synchronize the value of two traits: #--------------------------------------------------------------------------- def sync_trait ( self, trait_name, object, alias = None, mutual = True, remove = False ): """Synchronizes the value of a trait attribute on this object with a trait attribute on another object. Parameters ---------- name : str Name of the trait attribute on this object. object : object The object with which to synchronize. alias : str Name of the trait attribute on *other*; if None or omitted, same as *name*. mutual : bool or int Indicates whether synchronization is mutual (True or non-zero) or one-way (False or zero) remove : bool or int Indicates whether synchronization is being added (False or zero) or removed (True or non-zero) Description ----------- In mutual synchronization, any change to the value of the specified trait attribute of either object results in the same value being assigned to the corresponding trait attribute of the other object. In one-way synchronization, any change to the value of the attribute on this object causes the corresponding trait attribute of *object* to be updated, but not vice versa. """ if alias is None: alias = trait_name is_list = (self._is_list_trait( trait_name ) and object._is_list_trait( alias )) if remove: info = self._get_sync_trait_info() dic = info.get( trait_name ) if dic is not None: key = ( id( object ), alias ) if key in dic: del dic[ key ] if len( dic ) == 0: del info[ trait_name ] self._on_trait_change( self._sync_trait_modified, trait_name, remove = True ) if is_list: self._on_trait_change( self._sync_trait_items_modified, trait_name + '_items', remove = True ) if mutual: object.sync_trait( alias, self, trait_name, False, True ) return # Callback to use when the synced object goes out of scope. In order # to avoid reference cycles, this must not be a member function. See # Github issue #69 for more detail. def _sync_trait_listener_deleted (ref, info): for key, dic in info.items(): if key != '': for name, value in dic.items(): if ref is value[0]: del dic[ name ] if len( dic ) == 0: del info[ key ] info = self._get_sync_trait_info() dic = info.setdefault( trait_name, {} ) key = ( id( object ), alias ) callback = lambda ref: _sync_trait_listener_deleted(ref, info) value = ( weakref.ref( object, callback ), alias ) if key not in dic: if len( dic ) == 0: self._on_trait_change( self._sync_trait_modified, trait_name ) if is_list: self._on_trait_change( self._sync_trait_items_modified, trait_name + '_items' ) dic[ key ] = value setattr( object, alias, getattr( self, trait_name ) ) if mutual: object.sync_trait( alias, self, trait_name, False ) def _get_sync_trait_info ( self ): info = getattr( self, '__sync_trait__', None ) if info is None: self.__dict__[ '__sync_trait__' ] = info = {} info[ '' ] = {} return info def _sync_trait_modified ( self, object, name, old, new ): info = self.__sync_trait__ if name not in info: return locked = info[ '' ] locked[ name ] = None for object, object_name in info[ name ].values(): object = object() if object_name not in object._get_sync_trait_info()[ '' ]: try: setattr( object, object_name, new ) except: pass del locked[ name ] def _sync_trait_items_modified ( self, object, name, old, event ): n0 = event.index n1 = n0 + len( event.removed ) name = name[:-6] info = self.__sync_trait__ locked = info[ '' ] locked[ name ] = None for object, object_name in info[ name ].values(): object = object() if object_name not in object._get_sync_trait_info()[ '' ]: try: getattr( object, object_name )[ n0: n1 ] = event.added except: pass del locked[ name ] def _is_list_trait ( self, trait_name ): handler = self.base_trait( trait_name ).handler return ((handler is not None) and (handler.default_value_type == 5)) #--------------------------------------------------------------------------- # Add a new trait: #--------------------------------------------------------------------------- def add_trait ( self, name, *trait ): """Adds a trait attribute to this object. Parameters ---------- name : str Name of the attribute to add. *trait : Trait or a value that can be converted to a trait by Trait(). Trait definition for *name*. If more than one value is specified, it is equivalent to passing the entire list of values to Trait(). """ # Make sure a trait argument was specified: if len( trait ) == 0: raise ValueError, 'No trait definition was specified.' # Make sure only valid traits get added: if len( trait ) > 1: trait = Trait( *trait ) else: trait = _trait_for( trait[0] ) # Check to see if the trait has additional sub-traits that need to be # defined also: handler = trait.handler if handler is not None: if handler.has_items: self.add_trait( name + '_items', handler.items_event() ) if handler.is_mapped: self.add_trait( name + '_', _mapped_trait_for( trait ) ) # See if there already is a class or instance trait with the same name: old_trait = self._trait( name, 0 ) # Get the object's instance trait dictionary and add a clone of the new # trait to it: itrait_dict = self._instance_traits() itrait_dict[ name ] = trait = _clone_trait( trait ) # If there already was a trait with the same name: if old_trait is not None: # Copy the old traits notifiers into the new trait: old_notifiers = old_trait._notifiers( 0 ) if old_notifiers is not None: trait._notifiers( 1 ).extend( old_notifiers ) else: # Otherwise, see if there are any static notifiers that should be # applied to the trait: cls = self.__class__ handlers = [ _get_method( cls, '_%s_changed' % name ), _get_method( cls, '_%s_fired' % name ) ] # Add any special trait defined event handlers: _add_event_handlers( trait, cls, handlers ) # Add the 'anytrait' handler (if any): handlers.append( self.__prefix_traits__.get( '@' ) ) # Filter out any 'None' values: handlers = [ h for h in handlers if h is not None ] # If there are any static notifiers, attach them to the trait: if len( handlers ) > 0: _add_notifiers( trait._notifiers( 1 ), handlers ) # If this was a new trait, fire the 'trait_added' event: if old_trait is None: self.trait_added = name #--------------------------------------------------------------------------- # Remove an existing trait: #--------------------------------------------------------------------------- def remove_trait ( self, name ): """Removes a trait attribute from this object. Parameters ---------- name : str Name of the attribute to remove. Returns ------- result : bool True if the trait was successfully removed. """ # Get the trait definition: trait = self._trait( name, 0 ) if trait is not None: # Check to see if the trait has additional sub-traits that need to # be removed also: handler = trait.handler if handler is not None: if handler.has_items: self.remove_trait( name + '_items' ) if handler.is_mapped: self.remove_trait( name + '_' ) # Remove the trait value from the object dictionary as well: if name in self.__dict__: del self.__dict__[ name ] # Get the object's instance trait dictionary and remove the trait # from it: itrait_dict = self._instance_traits() if name in itrait_dict: del itrait_dict[ name ] return True return False #--------------------------------------------------------------------------- # Returns the trait definition of a specified trait: #--------------------------------------------------------------------------- def trait ( self, name, force = False, copy = False ): """Returns the trait definition for the *name* trait attribute. Parameters ---------- name : str Name of the attribute whose trait definition is to be returned. force : bool Indicates whether to return a trait definition if *name* is not explicitly defined. copy : bool Indicates whether to return the original trait definition or a copy. Description ----------- If *force* is False (the default) and *name* is the name of an implicitly defined trait attribute that has never been referenced explicitly (i.e., has not yet been defined), the result is None. In all other cases, the result is the trait definition object associated with *name*. If *copy* is True, and a valid trait definition is found for *name*, a copy of the trait found is returned. In all other cases, the trait definition found is returned unmodified (the default). """ mode = 0 if force: mode = -1 result = self._trait( name, mode ) if (not copy) or (result is None): return result return _clone_trait( result ) #--------------------------------------------------------------------------- # Returns the base trait definition of a specified trait: #--------------------------------------------------------------------------- def base_trait ( self, name ): """Returns the base trait definition for a trait attribute. Parameters ---------- name : str Name of the attribute whose trait definition is returned. Description ----------- This method is similar to the trait() method, and returns a different result only in the case where the trait attribute defined by *name* is a delegate. In this case, the base_trait() method follows the delegation chain until a non-delegated trait attribute is reached, and returns the definition of that attribute's trait as the result. """ return self._trait( name, -2 ) #--------------------------------------------------------------------------- # Validates whether or not a specified value is legal for a specified # trait and returns the validated value if valid: #--------------------------------------------------------------------------- def validate_trait ( self, name, value ): """ Validates whether a value is legal for a trait. Returns the validated value if it is valid. """ return self.base_trait( name ).validate( self, name, value ) #--------------------------------------------------------------------------- # Return a dictionary of all traits which match a set of metadata: #--------------------------------------------------------------------------- def traits ( self, **metadata ): """Returns a dictionary containing the definitions of all of the trait attributes of this object that match the set of *metadata* criteria. Parameters ---------- **metadata : Criteria for selecting trait attributes. Description ----------- The keys of the returned dictionary are the trait attribute names, and the values are their corresponding trait definition objects. If no *metadata* information is specified, then all explicitly defined trait attributes defined for the object are returned. Otherwise, the *metadata* keyword dictionary is assumed to define a set of search criteria for selecting trait attributes of interest. The *metadata* dictionary keys correspond to the names of trait metadata attributes to examine, and the values correspond to the values the metadata attribute must have in order to be included in the search results. The *metadata* values either may be simple Python values like strings or integers, or may be lambda expressions or functions that return True if the trait attribute is to be included in the result. A lambda expression or function must receive a single argument, which is the value of the trait metadata attribute being tested. If more than one metadata keyword is specified, a trait attribute must match the metadata values of all keywords to be included in the result. """ traits = self.__base_traits__.copy() # Update with instance-defined traits. for name, trt in self._instance_traits().items(): if name[-6:] != "_items": traits[name] = trt for name in self.__dict__.keys(): if name not in traits: trait = self.trait( name ) if trait is not None: traits[ name ] = trait if len( metadata ) == 0: return traits for meta_name, meta_eval in metadata.items(): if type( meta_eval ) is not FunctionType: metadata[ meta_name ] = _SimpleTest( meta_eval ) result = {} for name, trait in traits.items(): for meta_name, meta_eval in metadata.items(): if not meta_eval( getattr( trait, meta_name ) ): break else: result[ name ] = trait return result #--------------------------------------------------------------------------- # Return a dictionary of all traits which match a set of metadata: #--------------------------------------------------------------------------- def class_traits ( cls, **metadata ): """Returns a dictionary containing the definitions of all of the trait attributes of the class that match the set of *metadata* criteria. Parameters ---------- **metadata : Criteria for selecting trait attributes. Description ----------- The keys of the returned dictionary are the trait attribute names, and the values are their corresponding trait definition objects. If no *metadata* information is specified, then all explicitly defined trait attributes defined for the class are returned. Otherwise, the *metadata* keyword dictionary is assumed to define a set of search criteria for selecting trait attributes of interest. The *metadata* dictionary keys correspond to the names of trait metadata attributes to examine, and the values correspond to the values the metadata attribute must have in order to be included in the search results. The *metadata* values either may be simple Python values like strings or integers, or may be lambda expressions or functions that return **True** if the trait attribute is to be included in the result. A lambda expression or function must receive a single argument, which is the value of the trait metadata attribute being tested. If more than one metadata keyword is specified, a trait attribute must match the metadata values of all keywords to be included in the result. """ if len( metadata ) == 0: return cls.__base_traits__.copy() result = {} for meta_name, meta_eval in metadata.items(): if type( meta_eval ) is not FunctionType: metadata[ meta_name ] = _SimpleTest( meta_eval ) for name, trait in cls.__base_traits__.items(): for meta_name, meta_eval in metadata.items(): if not meta_eval( getattr( trait, meta_name ) ): break else: result[ name ] = trait return result class_traits = classmethod( class_traits ) #--------------------------------------------------------------------------- # Return a list of all trait names which match a set of metadata: #--------------------------------------------------------------------------- def trait_names ( self, **metadata ): """Returns a list of the names of all trait attributes whose definitions match the set of *metadata* criteria specified. Parameters ---------- **metadata : Criteria for selecting trait attributes. Description ----------- This method is similar to the traits() method, but returns only the names of the matching trait attributes, not the trait definitions. """ return self.traits( **metadata ).keys() def class_trait_names ( cls, **metadata ): """Returns a list of the names of all trait attributes whose definitions match the set of *metadata* criteria specified. Parameters ---------- **metadata : Criteria for selecting trait attributes. Description ----------- This method is similar to the traits() method, but returns only the names of the matching trait attributes, not the trait definitions. """ return cls.class_traits( **metadata ).keys() class_trait_names = classmethod( class_trait_names ) #--------------------------------------------------------------------------- # Explicitly sets the value of a cached property: #--------------------------------------------------------------------------- def _set_traits_cache ( self, name, value ): """ Explicitly sets the value of a cached property. """ cached = TraitsCache + name old_value = self.__dict__.get( cached, Undefined ) self.__dict__[ cached ] = value if old_value != value: self.trait_property_changed( name, old_value, value ) #--------------------------------------------------------------------------- # Explicitly flushes the value of a cached property: #--------------------------------------------------------------------------- def _flush_traits_cache ( self, name, value ): """ Explicitly flushes the value of a cached property. """ self.trait_property_changed( name, self.__dict__.pop( TraitsCache + name, Undefined ) ) #--------------------------------------------------------------------------- # Returns the trait definition for a specified name when there is no # explicit definition in the class: #--------------------------------------------------------------------------- def __prefix_trait__ ( self, name, is_set ): # Check to see if the name is of the form '__xxx__': if (name[:2] == '__') and (name[-2:] == '__'): if name == '__class__': return generic_trait # If this is for purposes of performing a 'setattr', always map the # name to an 'Any' trait: if is_set: return any_trait # Otherwise, it is a 'getattr' request, so indicate that no such # attribute exists: raise AttributeError, "'%s' object has no attribute '%s'" % ( self.__class__.__name__, name ) # Handle the special case of 'delegated' traits: if name[-1:] == '_': trait = self._trait( name[:-1], 0 ) if (trait is not None) and (trait.type == 'delegate'): return _clone_trait( trait ) prefix_traits = self.__prefix_traits__ for prefix in prefix_traits['*']: if prefix == name[ :len( prefix ) ]: # If we found a match, use its trait as a template for a new # trait: trait = prefix_traits[ prefix ] # Get any change notifiers that apply to the trait: cls = self.__class__ handlers = [ _get_method( cls, '_%s_changed' % name ), _get_method( cls, '_%s_fired' % name ) ] # Add any special trait defined event handlers: _add_event_handlers( trait, cls, handlers ) # Add the 'anytrait' handler (if any): handlers.append( prefix_traits.get( '@' ) ) # Filter out any 'None' values: handlers = [ h for h in handlers if h is not None ] # If there are any handlers, add them to the trait's notifier's # list: if len( handlers ) > 0: trait = _clone_trait( trait ) _add_notifiers( trait._notifiers( 1 ), handlers ) return trait # There should ALWAYS be a prefix match in the trait classes, since '' # is at the end of the list, so we should never get here: raise SystemError, ("Trait class look-up failed for attribute '%s' " "for an object of type '%s'") % ( name, self.__class__.__name__ ) #--------------------------------------------------------------------------- # Adds/Removes (Java-style) event listeners to an object: #--------------------------------------------------------------------------- def add_trait_listener ( self, object, prefix = '' ): self._trait_listener( object, prefix, False ) def remove_trait_listener ( self, object, prefix = '' ): self._trait_listener( object, prefix, True ) def _trait_listener ( self, object, prefix, remove ): if prefix[-1:] != '_': prefix += '_' n = len( prefix ) traits = self.__base_traits__ for name in self._each_trait_method( object ): if name[:n] == prefix: if name[-8:] == '_changed': short_name = name[n:-8] if short_name in traits: self._on_trait_change( getattr( object, name ), short_name, remove = remove ) elif short_name == 'anytrait': self._on_trait_change( getattr( object, name ), remove = remove ) elif name[:-6] == '_fired': short_name = name[n:-6] if short_name in traits: self._on_trait_change( getattr( object, name ), short_name, remove = remove ) elif short_name == 'anytrait': self._on_trait_change( getattr( object, name ), remove = remove ) #--------------------------------------------------------------------------- # Generates each (name, method) pair for a specified object: #--------------------------------------------------------------------------- def _each_trait_method ( self, object ): """ Generates each (name, method) pair for a specified object. """ dic = {} for klass in object.__class__.__mro__: for name, method in klass.__dict__.items(): if (type( method ) is FunctionType) and (name not in dic): dic[ name ] = True yield name #--------------------------------------------------------------------------- # Handles adding/removing listeners for a generic 'Instance' trait: #--------------------------------------------------------------------------- def _instance_changed_handler ( self, name, old, new ): """ Handles adding/removing listeners for a generic 'Instance' trait. """ arg_lists = self._get_instance_handlers( name ) if old is not None: for args in arg_lists: old.on_trait_change( remove = True, *args ) if new is not None: for args in arg_lists: new.on_trait_change( *args ) #--------------------------------------------------------------------------- # Handles adding/removing listeners for a generic 'List( Instance )' trait: #--------------------------------------------------------------------------- def _list_changed_handler ( self, name, old, new ): """ Handles adding/removing listeners for a generic 'List( Instance )' trait. """ arg_lists = self._get_instance_handlers( name ) for item in old: for args in arg_lists: item.on_trait_change( remove = True, *args ) for item in new: for args in arg_lists: item.on_trait_change( *args ) def _list_items_changed_handler ( self, name, not_used, event ): """ Handles adding/removing listeners for a generic 'List( Instance )' trait. """ arg_lists = self._get_instance_handlers( name[:-6] ) for item in event.removed: for args in arg_lists: item.on_trait_change( remove = True, *args ) for item in event.added: for args in arg_lists: item.on_trait_change( *args ) #--------------------------------------------------------------------------- # Returns a list of ( name, method ) pairs for a specified 'Instance' or # 'List( Instance )' trait name: #--------------------------------------------------------------------------- def _get_instance_handlers ( self, name ): """ Returns a list of ( name, method ) pairs for a specified 'Instance' or 'List( Instance )' trait name: """ return [ ( getattr( self, method_name ), item_name ) for method_name, item_name in self.__class__.__instance_traits__[ name ] ] #--------------------------------------------------------------------------- # Initializes the object's statically parsed, but dynamically registered, # traits listeners (called at object creation and unpickling times): #--------------------------------------------------------------------------- def _post_init_trait_listeners ( self ): """ Initializes the object's statically parsed, but dynamically registered, traits listeners (called at object creation and unpickling times). """ for name, data in self.__class__.__listener_traits__.items(): if data[0] == 'method': config = data[1] if config['post_init']: self.on_trait_change( getattr( self, name ), config['pattern'], deferred = True, dispatch=config['dispatch'] ) def _init_trait_listeners ( self ): """ Initializes the object's statically parsed, but dynamically registered, traits listeners (called at object creation and unpickling times). """ for name, data in self.__class__.__listener_traits__.items(): getattr( self, '_init_trait_%s_listener' % data[0] )( name, *data ) def _init_trait_method_listener ( self, name, kind, config ): """ Sets up the listener for a method with the @on_trait_change decorator. """ if not config['post_init']: self.on_trait_change( getattr( self, name ), config['pattern'], deferred = True, dispatch=config['dispatch'] ) def _init_trait_event_listener ( self, name, kind, pattern ): """ Sets up the listener for an event with on_trait_change metadata. """ @weak_arg(self) def notify ( self ): setattr( self, name, True ) self.on_trait_change( notify, pattern, target=self ) def _init_trait_property_listener ( self, name, kind, cached, pattern ): """ Sets up the listener for a property with 'depends_on' metadata. """ if cached is None: @weak_arg(self) def notify ( self ): self.trait_property_changed( name, None ) else: cached_old = cached + ':old' @weak_arg(self) def pre_notify ( self ): dict = self.__dict__ old = dict.get( cached_old, Undefined ) if old is Undefined: dict[ cached_old ] = dict.pop( cached, None ) self.on_trait_change( pre_notify, pattern, priority = True, target=self ) @weak_arg(self) def notify ( self ): old = self.__dict__.pop( cached_old, Undefined ) if old is not Undefined: self.trait_property_changed( name, old ) self.on_trait_change( notify, pattern, target=self ) def _init_trait_delegate_listener ( self, name, kind, pattern ): """ Sets up the listener for a delegate trait. """ name_pattern = self._trait_delegate_name( name, pattern ) target_name_len = len( name_pattern.split( ':' )[-1] ) @weak_arg(self) def notify ( self, object, notify_name, old, new ): self.trait_property_changed( name + notify_name[ target_name_len: ], old, new ) self.on_trait_change( notify, name_pattern, target=self ) self.__dict__.setdefault( ListenerTraits, {} )[ name ] = notify def _remove_trait_delegate_listener ( self, name, remove ): """ Removes a delegate listener when the local delegate value is set. """ dict = self.__dict__.setdefault( ListenerTraits, {} ) if remove: # Although the name should be in the dict, it may not be if a value # was assigned to a delegate in a constructor or setstate: if name in dict: # Remove the delegate listener: self.on_trait_change( dict[ name ], self._trait_delegate_name( name, self.__class__.__listener_traits__[ name ][1] ), remove = True ) del dict[ name ] if len( dict ) == 0: del self.__dict__[ ListenerTraits ] return # Otherwise the local copy of the delegate value was deleted, restore # the delegate listener (unless it's already there): if name not in dict: self._init_trait_delegate_listener( name, 0, self.__class__.__listener_traits__[ name ][1] ) def _trait_delegate_name ( self, name, pattern ): """ Returns the fully-formed 'on_trait_change' name for a specified delegate. """ if pattern[-1] == '*': pattern = '%s%s%s' % ( pattern[:-1], self.__class__.__prefix__, name ) return pattern # Patch the definition of _HasTraits to be the real 'HasTraits': _HasTraits = HasTraits #------------------------------------------------------------------------------- # 'HasStrictTraits' class: #------------------------------------------------------------------------------- class HasStrictTraits ( HasTraits ): """ This class guarantees that any object attribute that does not have an explicit or wildcard trait definition results in an exception. This feature can be useful in cases where a more rigorous software engineering approach is being used than is typical for Python programs. It also helps prevent typos and spelling mistakes in attribute names from going unnoticed; a misspelled attribute name typically causes an exception. """ _ = Disallow # Disallow access to any traits not explicitly defined #------------------------------------------------------------------------------- # 'HasPrivateTraits' class: #------------------------------------------------------------------------------- class HasPrivateTraits ( HasTraits ): """ This class ensures that any public object attribute that does not have an explicit or wildcard trait definition results in an exception, but "private" attributes (whose names start with '_') have an initial value of **None**, and are not type-checked. This feature is useful in cases where a class needs private attributes to keep track of its internal object state, which are not part of the class's public API. Such attributes do not need to be type-checked, because they are manipulated only by the (presumably correct) methods of the class itself. """ # Make 'private' traits (leading '_') have no type checking: __ = Any( private = True, transient = True ) # Disallow access to all other traits not explicitly defined: _ = Disallow #------------------------------------------------------------------------------ # ABC classes with traits: (where available) #------------------------------------------------------------------------------ try: import abc class ABCMetaHasTraits(abc.ABCMeta, MetaHasTraits): """ A MetaHasTraits subclass which also inherits from abc.ABCMeta. .. note:: The ABCMeta class is cooperative and behaves nicely with MetaHasTraits, provided it is inherited first. """ pass class ABCHasTraits(HasTraits): """ A HasTraits subclass which enables the features of Abstract Base Classes (ABC). See the 'abc' module in the standard library for more information. """ __metaclass__ = ABCMetaHasTraits class ABCHasStrictTraits(ABCHasTraits): """ A HasTraits subclass which behaves like HasStrictTraits but also enables the features of Abstract Base Classes (ABC). See the 'abc' module in the standard library for more information. """ _ = Disallow except ImportError: pass #------------------------------------------------------------------------------- # Singleton classes with traits: # # This code is based on a recipe taken from: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531 # Specifically, the implementation of Oren Tirosh is used. #------------------------------------------------------------------------------- class SingletonHasTraits ( HasTraits ): """ Singleton class that support trait attributes. """ def __new__ ( cls, *args, **traits ): if not '_the_instance' in cls.__dict__: cls._the_instance = HasTraits.__new__( cls, *args, **traits ) return cls._the_instance class SingletonHasStrictTraits ( HasStrictTraits ): """ Singleton class that supports strict trait attributes. Non-trait attributes generate an exception. """ def __new__ ( cls, *args, **traits ): return SingletonHasTraits.__new__( cls, *args, **traits ) class SingletonHasPrivateTraits ( HasPrivateTraits ): """ Singleton class that supports trait attributes, with private attributes being unchecked. """ def __new__ ( cls, *args, **traits ): return SingletonHasTraits.__new__( cls, *args, **traits ) #------------------------------------------------------------------------------- # Defines a 'vetoable' request object and an associated event: #------------------------------------------------------------------------------- class Vetoable ( HasStrictTraits ): """ Defines a 'vetoable' request object and an associated event. """ # Should the request be vetoed? (Can only be set to 'True') veto = Bool( False ) def _veto_changed ( self, state ): self._trait_veto_notify( state ) VetoableEvent = Event( Vetoable ) #------------------------------------------------------------------------------- # 'MetaInterface' class: #------------------------------------------------------------------------------- class MetaInterface ( ABCMetaHasTraits ): """ Meta class for interfaces. Interfaces are simple ABCs with the following features:- 1) They cannot be instantiated (they are interfaces, not implementations!). 2) Calling them is equivalent to calling 'adapt'. """ @deprecated('use "adapt(adaptee, protocol)" instead.') def __call__ ( self, adaptee, default=AdaptationError ): """ Attempt to adapt the adaptee to this interface. Note that this means that (intentionally ;^) that interfaces cannot be instantiated! """ from traits.adaptation.api import adapt return adapt(adaptee, self, default=default) #------------------------------------------------------------------------------- # 'Interface' class: #------------------------------------------------------------------------------- class Interface ( HasTraits ): """ The base class for all interfaces. """ __metaclass__ = MetaInterface #------------------------------------------------------------------------------- # Class decorator to declare the protocols that a class provides. #------------------------------------------------------------------------------- def provides( *protocols ): """ Class decorator to declare the protocols that a class provides. Parameters ---------- *protocols : A list of protocols (Interface classes or Python ABCs) that the decorated class provides. """ from abc import ABCMeta # Exit immediately if there is nothing to do. if len(protocols) == 0: return lambda klass: klass # Verify that each argument is a valid protocol. for protocol in protocols: if not issubclass(type(protocol), ABCMeta): raise TraitError( "All arguments to 'provides' must be " "subclasses of Interface or be a Python ABC." ) def wrapped_class(klass): for protocol in protocols: # We use 'type(protocol)' in case the 'protocol' implements # its own 'register' method that overrides the ABC method. type(protocol).register(protocol, klass) # Make sure the class does provide the protocols it claims to. if CHECK_INTERFACES: from .interface_checker import check_implements check_implements(klass, protocols, CHECK_INTERFACES) return klass return wrapped_class #------------------------------------------------------------------------------- # Return True if the class is an Interface. #------------------------------------------------------------------------------- def isinterface( klass ): """ Return True if the class is an Interface. """ return isinstance(klass, MetaInterface) #------------------------------------------------------------------------------- # Declares the interfaces that a class implements. #------------------------------------------------------------------------------- def implements( *interfaces ): """ Declares the interfaces that a class implements. Parameters ---------- *interfaces : A list of interface classes that the containing class implements. Description ----------- Registers each specified interface with the interface manager as an interface that the containing class implements. Each specified interface must be a subclass of **Interface**. This function should only be called from directly within a class body. .. deprecated:: 4.4 Use the ``provides`` class decorator instead. """ callback = provides(*interfaces) callback = deprecated( "'the 'implements' class advisor has been deprecated. " "Use the 'provides' class decorator." )(callback) addClassAdvisor(callback) #------------------------------------------------------------------------------- # 'ISerializable' interface: #------------------------------------------------------------------------------- class ISerializable ( Interface ): """ A class that implemented ISerializable requires that all HasTraits objects saved as part of its state also implement ISerializable. """ #------------------------------------------------------------------------------- # 'traits_super' class: #------------------------------------------------------------------------------- class traits_super ( super ): def __getattribute__ ( self, name ): try: return super( traits_super, self ).__getattribute__( name ) except: return self._noop def _noop ( self, *args, **kw ): pass traits-4.6.0/traits/interface_checker.py000066400000000000000000000161111300633736300203370ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2008, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: Martin Chilvers # Date: 03/20/2008 # #------------------------------------------------------------------------------ """ An attempt at type-safe casting. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import from types import FunctionType from inspect import getargspec, getmro from .has_traits import HasTraits #------------------------------------------------------------------------------- # Logging: #------------------------------------------------------------------------------- import logging logger = logging.getLogger( __name__ ) #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- # Message templates for interface errors. BAD_SIGNATURE = ("The '%s' class signature for the '%s' method is different " "from that of the '%s' interface.") MISSING_METHOD = ("The '%s' class does not implement the '%s' method of the " "'%s' interface.") MISSING_TRAIT = ("The '%s' class does not implement the %s trait(s) of the " "'%s' interface.") #------------------------------------------------------------------------------- # 'InterfaceError' class: #------------------------------------------------------------------------------- class InterfaceError ( Exception ): """ The exception raised if a class does not really implement an interface. """ pass #------------------------------------------------------------------------------- # 'InterfaceChecker' class: #------------------------------------------------------------------------------- class InterfaceChecker ( HasTraits ): """ Checks that interfaces are actually implemented. """ #--------------------------------------------------------------------------- # 'InterfaceChecker' interface: #--------------------------------------------------------------------------- def check_implements ( self, cls, interfaces, error_mode ): """ Checks that the class implements the specified interfaces. 'interfaces' can be a single interface or a list of interfaces. """ # If a single interface was specified then turn it into a list: try: iter( interfaces ) except TypeError: interfaces = [ interfaces ] # If the class has traits then check that it implements all traits and # methods on the specified interfaces: if issubclass( cls, HasTraits ): for interface in interfaces: if not self._check_has_traits_class( cls, interface, error_mode ): return False # Otherwise, just check that the class implements all methods on the # specified interface: else: for interface in interfaces: if not self._check_non_has_traits_class( cls, interface, error_mode ): return False return True #--------------------------------------------------------------------------- # Private interface: #--------------------------------------------------------------------------- def _check_has_traits_class ( self, cls, interface, error_mode ): """ Checks that a 'HasTraits' class implements an interface. """ return (self._check_traits( cls, interface, error_mode ) and self._check_methods( cls, interface, error_mode )) def _check_non_has_traits_class ( self, cls, interface, error_mode ): """ Checks that a non-'HasTraits' class implements an interface. """ return self._check_methods( cls, interface, error_mode ) def _check_methods ( self, cls, interface, error_mode ): """ Checks that a class implements the methods on an interface. """ cls_methods = self._get_public_methods( cls ) interface_methods = self._get_public_methods( interface ) for name in interface_methods: if name not in cls_methods: return self._handle_error( MISSING_METHOD % ( self._class_name( cls ), name, self._class_name( interface ) ), error_mode ) # Check that the method signatures are the same: cls_argspec = getargspec( cls_methods[ name ] ) interface_argspec = getargspec( interface_methods[ name ] ) if cls_argspec != interface_argspec: return self._handle_error( BAD_SIGNATURE % ( self._class_name( cls ), name, self._class_name( interface ) ), error_mode ) return True def _check_traits ( self, cls, interface, error_mode ): """ Checks that a class implements the traits on an interface. """ missing = set( interface.class_traits() ).difference( set( cls.class_traits() ) ) if len( missing ) > 0: return self._handle_error( MISSING_TRAIT % ( self._class_name( cls ), `list( missing )`[1:-1], self._class_name( interface ) ), error_mode ) return True def _get_public_methods ( self, cls ): """ Returns all public methods on a class. Returns a dictionary containing all public methods keyed by name. """ public_methods = {} for c in getmro( cls ): # Stop when we get to 'HasTraits'!: if c is HasTraits: break for name, value in c.__dict__.items(): if ((not name.startswith( '_' )) and (type( value ) is FunctionType)): if name not in public_methods: public_methods[ name ] = value return public_methods def _class_name ( self, cls ): return cls.__name__ def _handle_error ( self, msg, error_mode ): if error_mode > 1: raise InterfaceError( msg ) if error_mode == 1: logger.warning( msg ) return False # A default interface checker: checker = InterfaceChecker() def check_implements ( cls, interfaces, error_mode = 0 ): """ Checks that the class implements the specified interfaces. 'interfaces' can be a single interface or a list of interfaces. """ return checker.check_implements( cls, interfaces, error_mode ) traits-4.6.0/traits/protocols/000077500000000000000000000000001300633736300163655ustar00rootroot00000000000000traits-4.6.0/traits/protocols/README.txt000066400000000000000000000002561300633736300200660ustar00rootroot00000000000000The traits.protocols package has been deprecated in Traits 4.4.0. Please use the traits.adaptation package instead (see the "Migration guide" in the Traits documentation). traits-4.6.0/traits/protocols/__init__.py000066400000000000000000000005331300633736300204770ustar00rootroot00000000000000"""Trivial Interfaces and Adaptation from PyProtocols. This package used to be a subset of the files from Phillip J. Eby's PyProtocols package. The package has been substituted by :mod:`traits.adaptation` as of Traits 4.4.0. Currently, the package contains deprecated aliases for backward compatibility, and will be removed in Traits 5.0 . """ traits-4.6.0/traits/protocols/advice.py000066400000000000000000000227601300633736300202010ustar00rootroot00000000000000""" This module implements functionality similar to class decorators. At the moment it is used for the 'implements' and 'adapts' functions, which have been deprecated together with the 'traits.protocols' package. Please don't import anything from this module! """ from __future__ import absolute_import from types import FunctionType import sys from .. import _py2to3 __all__ = ['addClassAdvisor'] def metamethod(func): """Wrapper for metaclass method that might be confused w/instance method""" return property(lambda ob: func.__get__(ob,ob.__class__)) if sys.version_info[0] < 3: from types import ClassType, InstanceType ClassicTypes = ClassType def classicMRO(ob, extendedClassic=False): stack = [] push = stack.insert pop = stack.pop push(0,ob) while stack: cls = pop() yield cls p = len(stack) for b in cls.__bases__: push(p,b) if extendedClassic: yield InstanceType yield object def getMRO(ob, extendedClassic=False): if isinstance(ob,ClassicTypes): return classicMRO(ob,extendedClassic) elif isinstance(ob,type): return ob.__mro__ return ob, else: def getMRO(ob, *args, **kwargs): if args or kwargs: kwargs.pop('extendedClassic',None) if len(args)>1 or kwargs: raise TypeError import warnings warnings.warn(DeprecationWarning( """In Python 3 there are no more ols-style classes. Therefore, extendedClassic has no meaning and should not be used. """ )) if isinstance(ob,type): return ob.__mro__ return ob, try: from ._speedups import metamethod, getMRO, classicMRO except ImportError: pass # property-safe 'super()' for Python 2.2; 2.3 can use super() instead def supermeta(typ,ob): starttype = type(ob) mro = starttype.__mro__ if typ not in mro: starttype = ob mro = starttype.__mro__ mro = iter(mro) for cls in mro: if cls is typ: mro = [cls.__dict__ for cls in mro] break else: raise TypeError("Not sub/supertypes:", starttype, typ) typ = type(ob) class theSuper(object): def __getattribute__(self,name): for d in mro: if name in d: descr = d[name] try: descr = descr.__get__ except AttributeError: return descr else: return descr(ob,typ) return object.__getattribute__(self,name) return theSuper() def getFrameInfo(frame): """Return (kind,module,locals,globals) for a frame 'kind' is one of "exec", "module", "class", "function call", or "unknown". """ f_locals = frame.f_locals f_globals = frame.f_globals sameNamespace = f_locals is f_globals hasModule = '__module__' in f_locals hasName = '__name__' in f_globals sameName = hasModule and hasName sameName = sameName and f_globals['__name__']==f_locals['__module__'] module = hasName and sys.modules.get(f_globals['__name__']) or None namespaceIsModule = module and module.__dict__ is f_globals if not namespaceIsModule: # some kind of funky exec kind = "exec" elif sameNamespace and not hasModule: kind = "module" elif sameName and not sameNamespace: kind = "class" elif not sameNamespace: kind = "function call" else: # How can you have f_locals is f_globals, and have '__module__' set? # This is probably module-level code, but with a '__module__' variable. kind = "unknown" return kind,module,f_locals,f_globals def addClassAdvisor(callback, depth=2): """Set up 'callback' to be passed the containing class upon creation This function is designed to be called by an "advising" function executed in a class suite. The "advising" function supplies a callback that it wishes to have executed when the containing class is created. The callback will be given one argument: the newly created containing class. The return value of the callback will be used in place of the class, so the callback should return the input if it does not wish to replace the class. The optional 'depth' argument to this function determines the number of frames between this function and the targeted class suite. 'depth' defaults to 2, since this skips this function's frame and one calling function frame. If you use this function from a function called directly in the class suite, the default will be correct, otherwise you will need to determine the correct depth yourself. This function works by installing a special class factory function in place of the '__metaclass__' of the containing class. Therefore, only callbacks *after* the last '__metaclass__' assignment in the containing class will be executed. Be sure that classes using "advising" functions declare any '__metaclass__' *first*, to ensure all callbacks are run. Moreover, since starting from Python 3, metaclasses are specified differently, this function does not work anymore. Worse, as the metaclass is selected even before running the class's body, there is no way to fix this in a general way. As long as the metaclass provides some hooks to run code at class creation time, we can use them, but standard "type" does not. """ if sys.version_info[0] >= 3: raise NotImplementedError("Class advisors are not possible in python 3.") frame = sys._getframe(depth) kind, module, caller_locals, caller_globals = getFrameInfo(frame) if kind not in ("class", "exec"): raise SyntaxError( "Advice must be in the body of a class statement" ) previousMetaclass = caller_locals.get('__metaclass__') defaultMetaclass = caller_globals.get('__metaclass__', type) #TODO: This used to be ClassType, but I think this was errornous. Check it! def advise(name,bases,cdict): if '__metaclass__' in cdict: del cdict['__metaclass__'] if previousMetaclass is None: if bases: # find best metaclass or use global __metaclass__ if no bases meta = determineMetaclass(bases) else: meta = defaultMetaclass elif isClassAdvisor(previousMetaclass): # special case: we can't compute the "true" metaclass here, # so we need to invoke the previous metaclass and let it # figure it out for us (and apply its own advice in the process) meta = previousMetaclass else: meta = determineMetaclass(bases, previousMetaclass) newClass = meta(name,bases,cdict) # this lets the callback replace the class completely, if it wants to return callback(newClass) # introspection data only, not used by inner function advise.previousMetaclass = previousMetaclass advise.callback = callback # install the advisor caller_locals['__metaclass__'] = advise def isClassAdvisor(ob): """True if 'ob' is a class advisor function""" return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass') def determineMetaclass(bases, explicit_mc=None): """Determine metaclass from 1+ bases and optional explicit __metaclass__""" meta = [getattr(b,'__class__',type(b)) for b in bases] if explicit_mc is not None: # The explicit metaclass needs to be verified for compatibility # as well, and allowed to resolve the incompatible bases, if any meta.append(explicit_mc) if len(meta)==1: # easy case return meta[0] candidates = minimalBases(meta) # minimal set of metaclasses if not candidates: # they're all "classic" classes # should never happen in Python 3, so this should be fine from types import ClassType return ClassType elif len(candidates)>1: # We could auto-combine, but for now we won't... raise TypeError("Incompatible metatypes",bases) # Just one, return it return candidates[0] def minimalBases(classes): """Reduce a list of base classes to its ordered minimum equivalent""" classes = [c for c in classes if not _py2to3.is_old_style_class(c)] candidates = [] for m in classes: for n in classes: if issubclass(n,m) and m is not n: break else: # m has no subclasses in 'classes' if m in candidates: candidates.remove(m) # ensure that we're later in the list candidates.append(m) return candidates from weakref import ref class StrongRef(object): """Like a weakref, but for non-weakrefable objects""" __slots__ = 'referent' def __init__(self,referent): self.referent = referent def __call__(self): return self.referent def __hash__(self): return hash(self.referent) def __eq__(self,other): return self.referent==other def __repr__(self): return 'StrongRef(%r)' % self.referent def mkRef(ob,*args): """Return either a weakref or a StrongRef for 'ob' Note that extra args are forwarded to weakref.ref() if applicable.""" try: return ref(ob,*args) except TypeError: return StrongRef(ob) traits-4.6.0/traits/protocols/api.py000066400000000000000000000030371300633736300175130ustar00rootroot00000000000000"""Trivial Interfaces and Adaptation from PyProtocols. This package used to be a subset of the files from Phillip J. Eby's PyProtocols package. The package has been substituted by :mod:`traits.adaptation` as of Traits 4.4.0. Currently, the package contains deprecated aliases for backward compatibility, and will be removed in Traits 5.0 . """ from traits.util.api import deprecated @deprecated("use the 'adapt' function in 'traits.adaptation' instead") def adapt(*args, **kw): from traits.adaptation.api import adapt return adapt(*args, **kw) @deprecated("use the 'register_factory' function in 'traits.adaptation' instead") def declareAdapter(factory, provides, forTypes=(), forProtocols=(), forObjects=()): from traits.adaptation.api import register_factory from itertools import chain for from_protocol in chain(forTypes, forProtocols, forObjects): for to_protocol in provides: register_factory(factory, from_protocol, to_protocol) @deprecated("use the 'register_provides' function in 'traits.adaptation' instead") def declareImplementation(protocol, instancesProvide=(), instancesDoNotProvide=()): from traits.adaptation.api import register_provides for to_protocol in instancesProvide: register_provides(protocol, to_protocol) from traits.adaptation.adaptation_error import AdaptationError \ as AdaptationFailure # We will provide decorators as replacements for 'implements' and 'adapts' # in the future. from .advice import addClassAdvisor traits-4.6.0/traits/py2to3.h000066400000000000000000000156671300633736300156710ustar00rootroot00000000000000/* This header contains version specific implementations of certain often used tasks, behind a version agnostic API. */ #include "Python.h" /* Attribute names */ #if PY_MAJOR_VERSION < 3 PyObject *Py2to3_NormaliseAttrName(PyObject *name) { if( PyString_Check(name) ){ return name; #ifdef Py_USING_UNICODE } else if ( PyUnicode_Check( name ) ) { return PyUnicode_AsEncodedString( name, NULL, NULL ); #endif // #ifdef Py_USING_UNICODE } return NULL; } void Py2to3_FinishNormaliseAttrName(PyObject *name,PyObject *nname){ if(nname != name){ Py_DECREF(nname); } } PyObject *Py2to3_AttrNameCStr(PyObject *name) { return name; } #define Py2to3_AttrName_AS_STRING(name) PyString_AS_STRING(name) void Py2to3_FinishAttrNameCStr(PyObject *nname){ }; #else PyObject *Py2to3_NormaliseAttrName(PyObject *name) { if( PyUnicode_Check(name) ){ return name; } return NULL; } void Py2to3_FinishNormaliseAttrName(PyObject *name,PyObject *nname){ } PyObject *Py2to3_AttrNameCStr(PyObject *name) { return PyUnicode_AsUTF8String(name); } #define Py2to3_AttrName_AS_STRING(name) PyBytes_AS_STRING(name) void Py2to3_FinishAttrNameCStr(PyObject *nname){ Py_DECREF(nname); }; #endif /* Simple strings for PyErr formatting: * Python 2: - Attributes are ASCII - PyErr_Format uses PyString_FromFormat * Python 3: - Attributes are Unicode - PyErr_Format uses PyUnicode_FromFormat Thus SimpleString == PyString if Python < 3 else PyUnicode */ #if PY_MAJOR_VERSION >= 3 #define Py2to3_SimpleString_Check(name) PyUnicode_Check(name) #define Py2to3_SimpleString_GET_SIZE(name) PyUnicode_GET_SIZE(name) #define Py2to3_SimpleString_Type PyUnicode_Type #define Py2to3_PYERR_SIMPLE_STRING_FMTCHR "U" #define Py2to3_PYERR_PREPARE_SIMPLE_STRING(name) name #define Py2to3_SimpleString_FromString(string) PyUnicode_FromString(string) #else #define Py2to3_SimpleString_Check(name) PyString_Check(name) #define Py2to3_SimpleString_GET_SIZE(name) PyString_GET_SIZE(name) #define Py2to3_SimpleString_Type PyString_Type #define Py2to3_PYERR_SIMPLE_STRING_FMTCHR "s" #define Py2to3_PYERR_PREPARE_SIMPLE_STRING(name) PyString_AS_STRING(name) #define Py2to3_SimpleString_FromString(string) PyString_FromString(string) #endif /* The following macro is defined from Python 2.6 and differently in Python 3 */ #ifndef Py_TYPE #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #endif /* In Python 3, all ints are longs */ #if PY_MAJOR_VERSION >= 3 #define Py2to3_PyNum_Check PyLong_Check #define Py2to3_PyNum_FromLong PyLong_FromLong #define Py2to3_PyNum_AsLong PyLong_AsLong #else #define Py2to3_PyNum_Check PyInt_Check #define Py2to3_PyNum_FromLong PyInt_FromLong #define Py2to3_PyNum_AsLong PyInt_AsLong #endif /* Get hash of an object, using cached value for attribute names */ #ifndef Py_hash_t #define Py_hash_t long #endif #if PY_MAJOR_VERSION < 3 long Py2to3_GetHash_wCache(PyObject *obj){ long hash; if ( PyString_CheckExact( obj ) && ((hash = ((PyStringObject *) obj)->ob_shash) != -1) ) { return hash; } return PyObject_Hash( obj ); } #else Py_hash_t Py2to3_GetHash_wCache(PyObject *obj){ #ifndef Py_LIMITED_API Py_hash_t hash = -1; if ( PyUnicode_CheckExact( obj ) ) { #if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 3 hash = ((PyUnicodeObject *) obj)->hash; #else hash = ((PyUnicodeObject *) obj)->_base._base.hash; #endif // } else if ( PyBytes_CheckExact( key )) { // hash = ((PyBytesObject *) key)->ob_shash; } if (hash == -1) { hash = PyObject_Hash( obj ); } return hash; #else return PyObject_Hash( obj ); #endif // #ifndef Py_LIMITED_API } #endif // #if PY_MAJOR_VERION < 3 /* Get a value from a dict whose keys are attribute names. If *name* is of a type unfitting for an attribute name, *bad_attr* is returned. Does use the hash cache where possible. Precondition: *dict* is a valid PyDictObject */ #if PY_MAJOR_VERSION < 3 #ifdef Py_USING_UNICODE #define Py2to3_AttrNameCheck(name) (PyString_Check(name) || PyUnicode_Check(name)) #else #define Py2to3_AttrNameCheck(name) (PyString_Check(name)) #endif PyObject *Py2to3_GetAttrDictValue(PyDictObject * dict, PyObject *name, PyObject *bad_attr) { long hash; PyObject *nname; PyObject *value; if ( PyString_CheckExact( name ) ) { if ( (hash = ((PyStringObject *) name)->ob_shash) == -1 ) hash = PyObject_Hash( name ); return (dict->ma_lookup)( dict, name, hash )->me_value; } nname = Py2to3_NormaliseAttrName(name); if( nname == NULL ){ PyErr_Clear(); return bad_attr; } hash = PyObject_Hash( nname ); if( hash == -1 ){ Py2to3_FinishNormaliseAttrName(name,nname); PyErr_Clear(); return NULL; } value = (dict->ma_lookup)( dict, nname, hash )->me_value; Py2to3_FinishNormaliseAttrName(name,nname); return value; } #else // #if PY_MAJOR_VERSION < 3 #define Py2to3_AttrNameCheck(name) (PyUnicode_Check(name)) PyObject *Py2to3_GetAttrDictValue(PyDictObject * dict, PyObject *name, PyObject *bad_attr) { #if !defined(Py_LIMITED_API) && (PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 3) && 0 // this does not work as intended! don't use it! Py_hash_t hash; if( PyUnicode_CheckExact( name ) ) { #if PY_MINOR_VERSION < 3 hash = ((PyUnicodeObject *) dict)->hash; #else // currently not usable, as dict->ma_lookup does not exist either hash = ((PyUnicodeObject *) dict)->_base._base.hash; #endif if( hash == -1) hash = PyObject_Hash( name ); return (dict->ma_lookup)( dict, name, hash )->me_value; } #endif // #ifndef Py_LIMITED_API if( !PyUnicode_Check(name) ) return bad_attr; return PyDict_GetItem((PyObject *)dict, name); } #endif // #if PY_MAJOR_VERSION < 3 /* */ double Py2to3_PyNum_AsDouble(PyObject *value) { #if PY_MAJOR_VERSION < 3 if ( PyInt_Check( value ) ) { return (double) PyInt_AS_LONG( value ); } else if( !PyLong_Check(value) ){ #else if ( !PyLong_Check( value ) ) { #endif // #if PY_MAJOR_VERSION < 3 PyErr_SetNone( PyExc_TypeError ); return -1.; } return PyLong_AsDouble( value ); } #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #endif #if PY_MAJOR_VERSION >= 3 #define Py2to3_MOD_ERROR_VAL NULL #define Py2to3_MOD_SUCCESS_VAL(val) val #define Py2to3_MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define Py2to3_MOD_DEF(ob, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); #else #define Py2to3_MOD_ERROR_VAL #define Py2to3_MOD_SUCCESS_VAL(val) #define Py2to3_MOD_INIT(name) void init##name(void) #define Py2to3_MOD_DEF(ob, name, doc, methods) \ ob = Py_InitModule3(name, methods, doc); #endif traits-4.6.0/traits/testing/000077500000000000000000000000001300633736300160165ustar00rootroot00000000000000traits-4.6.0/traits/testing/__init__.py000066400000000000000000000006061300633736300201310ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005-2013 by Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ """ Scripts and assert tools related to running unit tests. These scripts also allow running test suites in separate processes and aggregating the results. """ traits-4.6.0/traits/testing/api.py000066400000000000000000000002131300633736300171350ustar00rootroot00000000000000from doctest_tools import doctest_for_module from nose_tools import deprecated, performance, skip from unittest_tools import UnittestTools traits-4.6.0/traits/testing/doctest_tools.py000066400000000000000000000033301300633736300212540ustar00rootroot00000000000000""" Tools for having doctest and unittest work together more nicely. Eclipse's PyDev plugin will run your unittest files for you very nicely. The doctest_for_module function allows you to easily run the doctest for a module along side your standard unit tests within Eclipse. """ # Standard library imports import doctest from traits.testing.unittest_tools import unittest import sys def doctest_for_module(module): """ Create a TestCase from a module's doctests that will be run by the standard unittest.main(). Example tests/test_foo.py:: import unittest import foo from traits.testing.api import doctest_for_module class FooTestCase(unittest.TestCase): ... class FooDocTest(doctest_for_module(foo)): pass if __name__ == "__main__": # This will run and report both FooTestCase and the doctests in # module foo. unittest.main() Alternatively, you can say:: FooDocTest = doctest_for_module(foo) instead of:: class FooDocTest(doctest_for_module(foo)): pass """ class C(unittest.TestCase): def test_dummy(self): pass # Make the test case loader find us def run(self, result=None): # doctest doesn't like nose.result.TextTestResult objects, # so we try to determine if thats what we're dealing # with and use its internal result attribute instead if hasattr(result, 'result'): doctest.DocTestSuite(module).run(result.result) else: doctest.DocTestSuite(module).run(result) return C traits-4.6.0/traits/testing/nose_tools.py000066400000000000000000000024551300633736300205620ustar00rootroot00000000000000"Non-standard functions for the 'nose' testing framework." try: from nose import DeprecatedTest, SkipTest from nose.tools import make_decorator def skip(f): """ Decorator to indicate a test should be skipped. """ def g(*args, **kw): raise SkipTest() return make_decorator(f)(g) def deprecated(f): """ Decorator to indicate a test is deprecated. """ def g(*args, **kw): raise DeprecatedTest() return make_decorator(f)(g) except ImportError: # Define stubs in case nose isn't installed. import warnings def skip(f): """ Stub replacement for marking a unit test to be skipped in the absence of 'nose'. """ warnings.warn("skipping unit tests requires the package 'nose'") return f def deprecated(f): """ Stub replacement for marking a unit test deprecated in the absence of 'nose'. """ warnings.warn("skipping deprecated unit tests requires the package 'nose'") return f def performance(f): """ Decorator to add an attribute to the test to mark it as a performance-measuring test. """ f.performance = True return f #### EOF ####################################################################### traits-4.6.0/traits/testing/tests/000077500000000000000000000000001300633736300171605ustar00rootroot00000000000000traits-4.6.0/traits/testing/tests/__init__.py000066400000000000000000000000001300633736300212570ustar00rootroot00000000000000traits-4.6.0/traits/testing/tests/test_unittest_tools.py000066400000000000000000000336741300633736300237050ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005-2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! #------------------------------------------------------------------------------ import threading import time import warnings from traits import _py2to3 from traits.testing.unittest_tools import unittest from traits.api import (Bool, Event, Float, HasTraits, Int, List, on_trait_change) from traits.testing.api import UnittestTools from traits.util.api import deprecated @deprecated("This function is outdated. Use 'shiny' instead!") def old_and_dull(): """ A deprecated function, for use in assertDeprecated tests. """ pass class TestObject(HasTraits): number = Float(2.0) list_of_numbers = List(Float) flag = Bool @on_trait_change('number') def _add_number_to_list(self, value): self.list_of_numbers.append(value) def add_to_number(self, value): self.number += value class UnittestToolsTestCase(unittest.TestCase, UnittestTools): def setUp(self): self.test_object = TestObject() def test_when_using_with(self): """ Check normal use cases as a context manager. """ test_object = self.test_object # Change event should NOT BE detected with self.assertTraitDoesNotChange(test_object, 'number') as result: test_object.flag = True test_object.number = 2.0 msg = 'The assertion result is not None: {0}'.format(result.event) self.assertIsNone(result.event, msg=msg) # Change event should BE detected with self.assertTraitChanges(test_object, 'number') as result: test_object.flag = False test_object.number = 5.0 expected = (test_object, 'number', 2.0, 5.0) self.assertSequenceEqual(expected, result.event) # Change event should BE detected exactly 2 times with self.assertTraitChanges(test_object, 'number', count=2) as result: test_object.flag = False test_object.number = 4.0 test_object.number = 3.0 expected = [(test_object, 'number', 5.0, 4.0), (test_object, 'number', 4.0, 3.0)] self.assertSequenceEqual(expected, result.events) self.assertSequenceEqual(expected[-1], result.event) # Change event should BE detected with self.assertTraitChanges(test_object, 'number') as result: test_object.flag = True test_object.add_to_number(10.0) expected = (test_object, 'number', 3.0, 13.0) self.assertSequenceEqual(expected, result.event) # Change event should BE detected exactly 3 times with self.assertTraitChanges(test_object, 'number', count=3) as result: test_object.flag = True test_object.add_to_number(10.0) test_object.add_to_number(10.0) test_object.add_to_number(10.0) expected = [(test_object, 'number', 13.0, 23.0), (test_object, 'number', 23.0, 33.0), (test_object, 'number', 33.0, 43.0)] self.assertSequenceEqual(expected, result.events) self.assertSequenceEqual(expected[-1], result.event) def test_assert_multi_changes(self): test_object = self.test_object # Change event should NOT BE detected with self.assertMultiTraitChanges([test_object], [], ['flag', 'number', 'list_of_numbers[]']) as results: test_object.number = 2.0 events = filter(bool, (result.event for result in results)) msg = 'The assertion result is not None: {0}'.format(", ".join(events)) self.assertFalse(events, msg=msg) # Change event should BE detected with self.assertMultiTraitChanges( [test_object], ['number', 'list_of_numbers[]'], ['flag']) as results: test_object.number = 5.0 events = filter(bool, (result.event for result in results)) msg = 'The assertion result is None' self.assertTrue(events, msg=msg) def test_when_using_functions(self): test_object = self.test_object # Change event should BE detected self.assertTraitChanges(test_object, 'number', 1, test_object.add_to_number, 13.0) # Change event should NOT BE detected self.assertTraitDoesNotChange(test_object, 'flag', test_object.add_to_number, 13.0) def test_indirect_events(self): """ Check catching indirect change events. """ test_object = self.test_object # Change event should BE detected with self.assertTraitChanges(test_object, 'list_of_numbers[]') as \ result: test_object.flag = True test_object.number = -3.0 expected = (test_object, 'list_of_numbers_items', [], [-3.0]) self.assertSequenceEqual(expected, result.event) def test_exception_inside_context(self): """ Check that exception inside the context statement block are propagated. """ test_object = self.test_object with self.assertRaises(AttributeError): with self.assertTraitChanges(test_object, 'number'): test_object.i_do_exist with self.assertRaises(AttributeError): with self.assertTraitDoesNotChange(test_object, 'number'): test_object.i_do_exist def test_non_change_on_failure(self): """ Check behaviour when assertion should be raised for non trait change. """ test_object = self.test_object traits = 'flag, number' with self.assertRaises(AssertionError): with self.assertTraitDoesNotChange(test_object, traits) as result: test_object.flag = True test_object.number = -3.0 expected = [(test_object, 'flag', False, True), (test_object, 'number', 2.0, -3.0)] self.assertEqual(result.events, expected) def test_change_on_failure(self): """ Check behaviour when assertion should be raised for trait change. """ test_object = self.test_object with self.assertRaises(AssertionError): with self.assertTraitChanges(test_object, 'number') as result: test_object.flag = True self.assertEqual(result.events, []) # Change event will not be fired 3 times with self.assertRaises(AssertionError): with self.assertTraitChanges(test_object, 'number', count=3) as \ result: test_object.flag = True test_object.add_to_number(10.0) test_object.add_to_number(10.0) expected = [(test_object, 'number', 2.0, 12.0), (test_object, 'number', 12.0, 22.0)] self.assertSequenceEqual(expected, result.events) def test_asserts_in_context_block(self): """ Make sure that the traits context manager does not stop regular assertions inside the managed code block from happening. """ test_object = TestObject(number=16.0) with self.assertTraitDoesNotChange(test_object, 'number'): self.assertEqual(test_object.number, 16.0) with self.assertRaisesRegexp(AssertionError, '16\.0 != 12\.0'): with self.assertTraitDoesNotChange(test_object, 'number'): self.assertEqual(test_object.number, 12.0) def test_special_case_for_count(self): """ Count equal to 0 should be valid but it is discouraged. """ test_object = TestObject(number=16.0) with self.assertTraitChanges(test_object, 'number', count=0): test_object.flag = True def test_assert_trait_changes_async(self): # Exercise assertTraitChangesAsync. thread_count = 10 events_per_thread = 1000 class A(HasTraits): event = Event a = A() def thread_target(obj, count): "Fire obj.event 'count' times." for _ in xrange(count): obj.event = True threads = [ threading.Thread(target=thread_target, args=(a, events_per_thread)) for _ in xrange(thread_count) ] expected_count = thread_count * events_per_thread with self.assertTraitChangesAsync( a, 'event', expected_count, timeout=60.0): for t in threads: t.start() for t in threads: t.join() def test_assert_trait_changes_async_events(self): # Check access to the events after the with # block completes. thread_count = 10 events_per_thread = 100 class A(HasTraits): event = Event(Int) a = A() def thread_target(obj, count): "Fire obj.event 'count' times." for n in xrange(count): time.sleep(0.001) obj.event = n threads = [ threading.Thread(target=thread_target, args=(a, events_per_thread)) for _ in xrange(thread_count) ] expected_count = thread_count * events_per_thread with self.assertTraitChangesAsync( a, 'event', expected_count, timeout=60.0) as event_collector: for t in threads: t.start() for t in threads: t.join() _py2to3.assertCountEqual( self, event_collector.events, range(events_per_thread) * thread_count, ) def test_assert_trait_changes_async_failure(self): # Exercise assertTraitChangesAsync. thread_count = 10 events_per_thread = 10000 class A(HasTraits): event = Event a = A() def thread_target(obj, count): "Fire obj.event 'count' times." for _ in xrange(count): obj.event = True threads = [ threading.Thread(target=thread_target, args=(a, events_per_thread)) for _ in xrange(thread_count) ] expected_count = thread_count * events_per_thread with self.assertRaises(AssertionError): with self.assertTraitChangesAsync(a, 'event', expected_count + 1): for t in threads: t.start() for t in threads: t.join() def test_assert_eventually_true_fails_on_timeout(self): class A(HasTraits): foo = Bool(False) a = A() def condition(a_object): return a_object.foo with self.assertRaises(self.failureException): self.assertEventuallyTrue( condition=condition, obj=a, trait='foo', timeout=1.0, ) def test_assert_eventually_true_passes_when_condition_becomes_true(self): class A(HasTraits): foo = Bool(False) def condition(a_object): return a_object.foo a = A() def thread_target(a): time.sleep(1.0) a.foo = True t = threading.Thread(target=thread_target, args=(a,)) t.start() self.assertEventuallyTrue( condition=condition, obj=a, trait='foo', timeout=10.0, ) t.join() def test_assert_eventually_true_passes_when_condition_starts_true(self): class A(HasTraits): foo = Bool(True) def condition(a_object): return a_object.foo a = A() self.assertEventuallyTrue( condition=condition, obj=a, trait='foo', timeout=10.0, ) def test_assert_deprecated(self): with self.assertDeprecated(): old_and_dull() def test_assert_deprecated_failures(self): with self.assertRaises(self.failureException): with self.assertDeprecated(): pass def test_assert_deprecated_when_warning_already_issued(self): # Exercise a problematic case where previous calls to a function or # method that issues a DeprecationWarning have already polluted the # __warningregistry__. For this, we need a single call-point to # old_and_dull, since distinct call-points have separate entries in # __warningregistry__. def old_and_dull_caller(): old_and_dull() # Pollute the registry by pre-calling the function. old_and_dull_caller() # Check that we can still detect the DeprecationWarning. with self.assertDeprecated(): old_and_dull_caller() def test_assert_not_deprecated_failures(self): with self.assertRaises(self.failureException): with self.assertNotDeprecated(): old_and_dull() def test_assert_not_deprecated(self): with self.assertNotDeprecated(): pass def test_assert_not_deprecated_when_warning_already_issued(self): # Exercise a problematic case where previous calls to a function or # method that issues a DeprecationWarning have already polluted the # __warningregistry__. For this, we need a single call-point to # old_and_dull, since distinct call-points have separate entries in # __warningregistry__. def old_and_dull_caller(): old_and_dull() # Pollute the registry by pre-calling the function. old_and_dull_caller() # Check that we can still detect the DeprecationWarning. with self.assertRaises(self.failureException): with self.assertNotDeprecated(): old_and_dull_caller() if __name__ == '__main__': unittest.main() traits-4.6.0/traits/testing/unittest_tools.py000066400000000000000000000401501300633736300214670ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005-2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! #------------------------------------------------------------------------------ """ Trait assert mixin class to simplify test implementation for Trait Classes. """ import contextlib import threading import sys import warnings from traits.api import (Any, Event, HasStrictTraits, Instance, Int, List, Property, Str) from traits.util.async_trait_wait import wait_for_condition # Compatibility layer for Python 2.6: try loading unittest2 from traits import _py2to3 if sys.version_info[:2] == (2, 6): import unittest2 as unittest else: import unittest class _AssertTraitChangesContext(object): """ A context manager used to implement the trait change assert methods. Attributes ---------- obj : HasTraits The HasTraits class instance who's class trait will change. xname : str The extended trait name of trait changes to listen to. count : int, optional The expected number of times the event should be fired. When None (default value) there is no check for the number of times the change event was fired. events : list of tuples A list with tuple elements containing the arguments of an `on_trait_change` event signature (, , , ). Raises ------ AssertionError : When the desired number of trait changed did not take place or when `count = None` and no trait change took place. """ def __init__(self, obj, xname, count, test_case): """ Initialize the trait change assertion context manager. Parameters ---------- obj : HasTraits The HasTraits class instance who's class trait will change. xname : str The extended trait name of trait changes to listen to. count : int, optional The expected number of times the event should be fired. When None (default value) there is no check for the number of times the change event was fired. test_case : TestCase A unittest TestCase where to raise the failureException if necessary. Notes ----- - Checking if the provided xname corresponds to valid traits in the class is not implemented yet. """ self.obj = obj self.xname = xname self.count = count self.event = None self.events = [] self.failureException = test_case.failureException def _listener(self, obj, name, old, new): """ Dummy trait listener. """ self.event = (obj, name, old, new) self.events.append(self.event) def __enter__(self): """ Bind the trait listener. """ self.obj.on_trait_change(self._listener, self.xname) return self def __exit__(self, exc_type, exc_value, tb): """ Remove the trait listener. """ if exc_type is not None: return False self.obj.on_trait_change(self._listener, self.xname, remove=True) if self.count is not None and len(self.events) != self.count: msg = 'Change event for {0} was fired {1} times instead of {2}' items = self.xname, len(self.events), self.count raise self.failureException(msg.format(*items)) elif self.count is None and not self.events: msg = 'A change event was not fired for: {0}'.format(self.xname) raise self.failureException(msg) return False @contextlib.contextmanager def reverse_assertion(context, msg): context.__enter__() try: yield context finally: try: context.__exit__(None, None, None) except AssertionError: pass else: raise context.failureException(msg) class _TraitsChangeCollector(HasStrictTraits): """ Class allowing thread-safe recording of events. """ # The object we're listening to. obj = Any # The (possibly extended) trait name. trait = Str # Read-only event count. event_count = Property(Int) # Event that's triggered when the event count is updated. event_count_updated = Event # Private list of events. events = List(Any) # Lock used to allow access to events by multiple threads # simultaneously. _lock = Instance(threading.Lock, ()) def start_collecting(self): self.obj.on_trait_change( self._event_handler, self.trait, ) def stop_collecting(self): self.obj.on_trait_change( self._event_handler, self.trait, remove=True, ) def _event_handler(self, new): with self._lock: self.events.append(new) self.event_count_updated = True def _get_event_count(self): """ Traits property getter. Thread-safe access to event count. """ with self._lock: return len(self.events) class UnittestTools(object): """ Mixin class to augment the unittest.TestCase class with useful trait related assert methods. """ def assertTraitChanges(self, obj, trait, count=None, callableObj=None, *args, **kwargs): """ Assert an object trait changes a given number of times. Assert that the class trait changes exactly `count` times during execution of the provided function. This method can also be used in a with statement to assert that a class trait has changed during the execution of the code inside the with statement (similar to the assertRaises method). Please note that in that case the context manager returns itself and the user can introspect the information of: - The last event fired by accessing the ``event`` attribute of the returned object. - All the fired events by accessing the ``events`` attribute of the return object. Note that in the case of chained properties (trait 'foo' depends on 'bar', which in turn depends on 'baz'), the order in which the corresponding trait events appear in the ``events`` attribute is not well-defined, and may depend on dictionary ordering. **Example**:: class MyClass(HasTraits): number = Float(2.0) my_class = MyClass() with self.assertTraitChangesExactly(my_class, 'number', count=1): my_class.number = 3.0 Parameters ---------- obj : HasTraits The HasTraits class instance whose class trait will change. trait : str The extended trait name of trait changes to listen to. count : int or None, optional The expected number of times the event should be fired. When None (default value) there is no check for the number of times the change event was fired. callableObj : callable, optional A callable object that will trigger the expected trait change. When None (default value) a trigger is expected to be called under the context manger returned by this method. *args : List of positional arguments for ``callableObj`` **kwargs : Dict of keyword value pairs to be passed to the ``callableObj`` Returns ------- context : context manager or None If ``callableObj`` is None, an assertion context manager is returned, inside of which a trait-change trigger can be invoked. Otherwise, the context is used internally with ``callableObj`` as the trigger, in which case None is returned. Notes ----- - Checking if the provided ``trait`` corresponds to valid traits in the class is not implemented yet. - Using the functional version of the assert method requires the ``count`` argument to be given even if it is None. """ context = _AssertTraitChangesContext(obj, trait, count, self) if callableObj is None: return context with context: callableObj(*args, **kwargs) def assertTraitDoesNotChange(self, obj, trait, callableObj=None, *args, **kwargs): """ Assert an object trait does not change. Assert that the class trait does not change during execution of the provided function. Parameters ---------- obj : HasTraits The HasTraits class instance whose class trait will change. trait : str The extended trait name of trait changes to listen to. callableObj : callable, optional A callable object that should not trigger a change in the passed trait. When None (default value) a trigger is expected to be called under the context manger returned by this method. *args : List of positional arguments for ``callableObj`` **kwargs : Dict of keyword value pairs to be passed to the ``callableObj`` Returns ------- context : context manager or None If ``callableObj`` is None, an assertion context manager is returned, inside of which a trait-change trigger can be invoked. Otherwise, the context is used internally with ``callableObj`` as the trigger, in which case None is returned. """ msg = 'A change event was fired for: {0}'.format(trait) context = _AssertTraitChangesContext(obj, trait, None, self) if callableObj is None: return reverse_assertion(context, msg) with reverse_assertion(context, msg): callableObj(*args, **kwargs) return def assertMultiTraitChanges(self, objects, traits_modified, traits_not_modified): """ Assert that traits on multiple objects do or do not change. This combines some of the functionality of `assertTraitChanges` and `assertTraitDoesNotChange`. Parameters ---------- objects : list of HasTraits The HasTraits class instances whose traits will change. traits_modified : list of str The extended trait names of trait expected to change. traits_not_modified : list of str The extended trait names of traits not expected to change. """ args = [] for obj in objects: for trait in traits_modified: args.append(self.assertTraitChanges(obj, trait)) for trait in traits_not_modified: args.append(self.assertTraitDoesNotChange(obj, trait)) return _py2to3.nested_context_mgrs(*args) @contextlib.contextmanager def assertTraitChangesAsync(self, obj, trait, count=1, timeout=5.0): """ Assert an object trait eventually changes. Context manager used to assert that the given trait changes at least `count` times within the given timeout, as a result of execution of the body of the corresponding with block. The trait changes are permitted to occur asynchronously. **Example usage**:: with self.assertTraitChangesAsync(my_object, 'SomeEvent', count=4): Parameters ---------- obj : HasTraits The HasTraits class instance whose class trait will change. trait : str The extended trait name of trait changes to listen to. count : int, optional The expected number of times the event should be fired. timeout : float or None, optional The amount of time in seconds to wait for the specified number of changes. None can be used to indicate no timeout. """ collector = _TraitsChangeCollector(obj=obj, trait=trait) # Pass control to body of the with statement. collector.start_collecting() try: yield collector # Wait for the expected number of events to arrive. try: wait_for_condition( condition=lambda obj: obj.event_count >= count, obj=collector, trait='event_count_updated', timeout=timeout, ) except RuntimeError: actual_event_count = collector.event_count msg = ("Expected {0} event on {1} to be fired at least {2} " "times, but the event was only fired {3} times " "before timeout ({4} seconds).").format( trait, obj, count, actual_event_count, timeout, ) self.fail(msg) finally: collector.stop_collecting() def assertEventuallyTrue(self, obj, trait, condition, timeout=5.0): """ Assert that the given condition is eventually true. Parameters ---------- obj : HasTraits The HasTraits class instance who's traits will change. trait : str The extended trait name of trait changes to listen to. condition : callable A function that will be called when the specified trait changes. This should accept ``obj`` and should return a Boolean indicating whether the condition is satisfied or not. timeout : float or None, optional The amount of time in seconds to wait for the condition to become true. None can be used to indicate no timeout. """ try: wait_for_condition( condition=condition, obj=obj, trait=trait, timeout=timeout, ) except RuntimeError: # Helpful to know whether we timed out because the # condition never became true, or because the expected # event was never issued. condition_at_timeout = condition(obj) self.fail( "Timed out waiting for condition. " "At timeout, condition was {0}.".format(condition_at_timeout)) @contextlib.contextmanager def _catch_warnings(self): # Ugly hack copied from the core Python code (see # Lib/test/test_support.py) to reset the warnings registry # for the module making use of this context manager. # # Note that this hack is unnecessary in Python 3.4 and later; see # http://bugs.python.org/issue4180 for the background. registry = sys._getframe(4).f_globals.get('__warningregistry__') if registry: registry.clear() with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', DeprecationWarning) yield w @contextlib.contextmanager def assertDeprecated(self): """ Assert that the code inside the with block is deprecated. Intended for testing uses of traits.util.deprecated.deprecated. """ with self._catch_warnings() as w: yield w self.assertGreater(len(w), 0, msg="Expected a DeprecationWarning, " "but none was issued") @contextlib.contextmanager def assertNotDeprecated(self): """ Assert that the code inside the with block is not deprecated. Intended for testing uses of traits.util.deprecated.deprecated. """ with self._catch_warnings() as w: yield w self.assertEqual(len(w), 0, msg="Expected no DeprecationWarning, " "but at least one was issued") traits-4.6.0/traits/tests/000077500000000000000000000000001300633736300155035ustar00rootroot00000000000000traits-4.6.0/traits/tests/__init__.py000066400000000000000000000000701300633736300176110ustar00rootroot00000000000000#required by simple_test_case.py so it can do an import traits-4.6.0/traits/tests/check_timing.py000066400000000000000000000132341300633736300205040ustar00rootroot00000000000000# Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 03/03/2003 # Description: Perform timing tests on various trait styles to determine the # amount of overhead that traits add. from __future__ import absolute_import from time import time from ..api import Any, DelegatesTo, HasTraits, Int, Range # Number of iterations to perform: n = 1000000 # Loop overhead time (actual value determined first time a measurement is made) t0 = -1.0 # Measure how long it takes to execute a specified function: def measure(func): now = time() func() return time() - now # 'Old style' Python attribute get/set: class old_style_value: def measure(self, reference_get=1.0, reference_set=1.0): global t0 self.init() if t0 < 0.0: t0 = measure(self.null) t1 = measure(self.do_get) t2 = measure(self.do_set) scale = 1.0e6 / n get_time = max(t1 - t0, 0.0) * scale set_time = max(t2 - t0, 0.0) * scale return get_time, set_time def null(self): for i in range(n): pass def init(self): self.value = -1 def do_set(self): for i in range(n): self.value = i def do_get(self): for i in range(n): self.value # 'New style' Python attribute get/set: class new_style_value(object): def measure(self): global t0 self.init() if t0 < 0.0: t0 = measure(self.null) t1 = measure(self.do_get) t2 = measure(self.do_set) scale = 1.0e6 / n get_time = max(t1 - t0, 0.0) * scale set_time = max(t2 - t0, 0.0) * scale return get_time, set_time def null(self): for i in range(n): pass def init(self): self.value = -1 def do_set(self): for i in range(n): self.value = i def do_get(self): for i in range(n): self.value # Python 'property' get/set: class property_value(new_style_value): def get_value(self): return self._value def set_value(self, value): self._value = value value = property(get_value, set_value) # Python 'global' get/set: class global_value(new_style_value): def init(self): global gvalue gvalue = -1 def do_set(self): global gvalue for i in range(n): gvalue = i def do_get(self): global gvalue for i in range(n): gvalue # Trait that can have any value: class any_value(HasTraits, new_style_value): value = Any # Trait that can only have 'float' values: class int_value(any_value): value = Int # Trait that can only have 'range' values: class range_value(any_value): value = Range(-1, 2000000000) # Executes method when float trait is changed: class change_value(int_value): def _value_changed(self, old, new): pass # Notifies handler when float trait is changed: class monitor_value(int_value): def init(self): self.on_trait_change(self.on_value_change, 'value') def on_value_change(self, object, trait_name, old, new): pass # Float trait is delegated to another object: class delegate_value(HasTraits, new_style_value): value = DelegatesTo('delegate') delegate = Any def init(self): self.delegate = int_value() # Float trait is delegated through one object to another object: class delegate_2_value(delegate_value): def init(self): delegate = delegate_value() delegate.init() self.delegate = delegate # Float trait is delegated through two objects to another object: class delegate_3_value(delegate_value): def init(self): delegate = delegate_2_value() delegate.init() self.delegate = delegate # Run the timing measurements: def report(name, get_time, set_time, ref_get_time, ref_set_time): """ Return string containing a benchmark report. The arguments are the name of the benchmark case, the times to do a 'get' or a 'set' operation for that benchmark case in usec, and the corresponding times for a reference operation (e.g., getting and setting an attribute on a new-style instance. """ template = ( '{name:^30}: Get {get_time:02.3f} us (x {get_speed_up:02.3f}), ' 'Set {set_time:02.3f} us (x {set_speed_up:02.3f})' ) report = template.format( name=name, get_time=get_time, get_speed_up=ref_get_time / get_time, set_time=set_time, set_speed_up=ref_set_time / set_time, ) return report def run_benchmark(klass, ref_get_time, ref_set_time): benchmark_name = klass.__name__ get_time, set_time = klass().measure() print report(benchmark_name, get_time, set_time, ref_get_time, ref_set_time) def main(): ref_get_time, ref_set_time = new_style_value().measure() benchmarks = [ global_value, old_style_value, new_style_value, property_value, any_value, int_value, range_value, change_value, monitor_value, delegate_value, delegate_2_value, delegate_3_value ] for benchmark in benchmarks: run_benchmark(benchmark, ref_get_time, ref_set_time) if __name__ == '__main__': main() traits-4.6.0/traits/tests/test_abc.py000066400000000000000000000041011300633736300176350ustar00rootroot00000000000000""" Test the ABC functionality. """ import abc import warnings from traits.testing.unittest_tools import unittest from ..api import ABCHasTraits, ABCMetaHasTraits, HasTraits, Int, Float class TestNew(unittest.TestCase): """ Test that __new__ works correctly. """ def setUp(self): self.old_filters = warnings.filters[:] warnings.simplefilter('error', DeprecationWarning) def tearDown(self): warnings.filters[:] = self.old_filters def test_new(self): # Should not raise DeprecationWarning. HasTraits(x=10) class AbstractFoo(ABCHasTraits): x = Int(10) y = Float(20.0) @abc.abstractmethod def foo(self): raise NotImplementedError() @abc.abstractproperty def bar(self): raise NotImplementedError() class ConcreteFoo(AbstractFoo): def foo(self): return 'foo' @property def bar(self): return 'bar' class FooLike(HasTraits): x = Int(10) y = Float(20.0) def foo(self): return 'foo' @property def bar(self): return 'bar' AbstractFoo.register(FooLike) class AbstractBar(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def bar(self): raise NotImplementedError() class TestABC(unittest.TestCase): def test_basic_abc(self): self.assertRaises(TypeError, AbstractFoo) concrete = ConcreteFoo() self.assertEqual(concrete.foo(), 'foo') self.assertEqual(concrete.bar, 'bar') self.assertEqual(concrete.x, 10) self.assertEqual(concrete.y, 20.0) self.assertTrue(isinstance(concrete, AbstractFoo)) def test_registered(self): foolike = FooLike() self.assertTrue(isinstance(foolike, AbstractFoo)) def test_post_hoc_mixing(self): class TraitedBar(HasTraits, AbstractBar): __metaclass__ = ABCMetaHasTraits x = Int(10) def bar(self): return 'bar' traited = TraitedBar() self.assertTrue(isinstance(traited, AbstractBar)) self.assertEqual(traited.x, 10) traits-4.6.0/traits/tests/test_anytrait_static_notifiers.py000066400000000000000000000112611300633736300244010ustar00rootroot00000000000000""" Tests for the the "anytrait" static notifiers. """ from traits.api import Float, HasTraits, Undefined from traits.testing.unittest_tools import unittest from traits import trait_notifiers anycalls_0 = [] class AnytraitStaticNotifiers0(HasTraits): ok = Float def _anytrait_changed(): anycalls_0.append(True) class AnytraitStaticNotifiers0Fail(HasTraits): fail = Float def _anytrait_changed(): raise Exception('error') class AnytraitStaticNotifiers1(HasTraits): ok = Float def _anytrait_changed(self): if not hasattr(self, 'anycalls'): self.anycalls = [] self.anycalls.append(True) class AnytraitStaticNotifiers1Fail(HasTraits): fail = Float def _anytrait_changed(self): raise Exception('error') class AnytraitStaticNotifiers2(HasTraits): ok = Float def _anytrait_changed(self, name): if not hasattr(self, 'anycalls'): self.anycalls = [] self.anycalls.append(name) class AnytraitStaticNotifiers2Fail(HasTraits): fail = Float def _anytrait_changed(self, name): raise Exception('error') class AnytraitStaticNotifiers3(HasTraits): ok = Float def _anytrait_changed(self, name, new): if not hasattr(self, 'anycalls'): self.anycalls = [] self.anycalls.append((name, new)) class AnytraitStaticNotifiers3Fail(HasTraits): fail = Float def _anytrait_changed(self, name, new): raise Exception('error') class AnytraitStaticNotifiers4(HasTraits): ok = Float def _anytrait_changed(self, name, old, new): if not hasattr(self, 'anycalls'): self.anycalls = [] self.anycalls.append((name, old, new)) class AnytraitStaticNotifiers4Fail(HasTraits): fail = Float def _anytrait_changed(self, name, old, new): raise Exception('error') class TestNotifiers(unittest.TestCase): """ Tests for the static notifiers, and the "anytrait" static notifiers. """ #### 'TestCase' protocol ################################################## def setUp(self): self.exceptions = [] trait_notifiers.push_exception_handler(self._handle_exception) def tearDown(self): trait_notifiers.pop_exception_handler() #### Private protocol ##################################################### def _handle_exception(self, obj, name, old, new): self.exceptions.append((obj, name, old, new)) #### Tests ################################################################ def test_anytrait_static_notifiers_0(self): obj = AnytraitStaticNotifiers0(ok=2) obj.ok = 3 self.assertEqual(len(anycalls_0), 2) def test_anytrait_static_notifiers_1(self): obj = AnytraitStaticNotifiers1(ok=2) obj.ok = 3 # 3 calls (see test_anytrait_static_notifiers_4): # 1 to add trait 'anycalls', # 1 from the constructor, # 1 to set ok to 3 self.assertEqual(len(obj.anycalls), 3) def test_anytrait_static_notifiers_2(self): obj = AnytraitStaticNotifiers2(ok=2) obj.ok = 3 expected = ['trait_added', 'ok', 'ok'] self.assertEqual(expected, obj.anycalls) def test_anytrait_static_notifiers_3(self): obj = AnytraitStaticNotifiers3(ok=2) obj.ok = 3 expected = [('trait_added', 'anycalls'), ('ok', 2), ('ok', 3)] self.assertEqual(expected, obj.anycalls) def test_anytrait_static_notifiers_4(self): obj = AnytraitStaticNotifiers4(ok=2) obj.ok = 3 expected = [('trait_added', Undefined, 'anycalls'), ('ok', 0, 2), ('ok', 2, 3)] self.assertEqual(expected, obj.anycalls) def test_anytrait_static_notifiers_0_fail(self): obj = AnytraitStaticNotifiers0Fail() obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_anytrait_static_notifiers_1_fail(self): obj = AnytraitStaticNotifiers1Fail() obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_anytrait_static_notifiers_2_fail(self): obj = AnytraitStaticNotifiers2Fail() obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_anytrait_static_notifiers_3_fail(self): obj = AnytraitStaticNotifiers3Fail() obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_anytrait_static_notifiers_4_fail(self): obj = AnytraitStaticNotifiers4Fail() obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_array.py000066400000000000000000000032411300633736300202320ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! #------------------------------------------------------------------------------ from __future__ import absolute_import from traits.testing.unittest_tools import unittest try: import numpy except ImportError: numpy_available = False else: numpy_available = True from ..api import Array, Bool, HasTraits if numpy_available: # Use of `Array` requires NumPy to be installed. class Foo(HasTraits): a = Array() event_fired = Bool(False) def _a_changed(self): self.event_fired = True class ArrayTestCase(unittest.TestCase): """ Test cases for delegated traits. """ @unittest.skipUnless(numpy_available, "numpy not available") def test_zero_to_one_element(self): """ Test that an event fires when an Array trait changes from zero to one element. """ f = Foo() f.a = numpy.zeros((2,), float) f.event_fired = False # Change the array. f.a = numpy.concatenate((f.a, numpy.array([100]))) # Confirm that the static trait handler was invoked. self.assertEqual(f.event_fired, True) return #### EOF ###################################################################### traits-4.6.0/traits/tests/test_array_or_none.py000066400000000000000000000134111300633736300217510ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Tests for the ArrayOrNone TraitType. """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest try: import numpy except ImportError: numpy_available = False else: numpy_available = True from traits.testing.unittest_tools import UnittestTools from ..api import ArrayOrNone, HasTraits, NO_COMPARE, TraitError if numpy_available: # Use of `ArrayOrNone` requires NumPy to be installed. class Foo(HasTraits): maybe_array = ArrayOrNone maybe_float_array = ArrayOrNone(dtype=float) maybe_two_d_array = ArrayOrNone(shape=(None, None)) maybe_array_with_default = ArrayOrNone(value=[1, 2, 3]) maybe_array_no_compare = ArrayOrNone(comparison_mode=NO_COMPARE) @unittest.skipUnless(numpy_available, "numpy not available") class TestArrayOrNone(unittest.TestCase, UnittestTools): """ Tests for the ArrayOrNone TraitType. """ def test_default(self): foo = Foo() self.assertIsNone(foo.maybe_array) def test_explicit_default(self): foo = Foo() self.assertIsInstance(foo.maybe_array_with_default, numpy.ndarray) def test_default_validation(self): # CArray and Array validate the default at class creation time; # we do the same for ArrayOrNone. with self.assertRaises(TraitError): class Bar(HasTraits): bad_array = ArrayOrNone(shape=(None, None), value=[1, 2, 3]) def test_setting_array_from_array(self): foo = Foo() test_array = numpy.arange(5) foo.maybe_array = test_array output_array = foo.maybe_array self.assertIsInstance(output_array, numpy.ndarray) self.assertEqual(output_array.dtype, test_array.dtype) self.assertEqual(output_array.shape, test_array.shape) self.assertTrue((output_array == test_array).all()) def test_setting_array_from_list(self): foo = Foo() test_list = [5, 6, 7, 8, 9] foo.maybe_array = test_list output_array = foo.maybe_array self.assertIsInstance(output_array, numpy.ndarray) self.assertEqual(output_array.dtype, numpy.dtype(int)) self.assertEqual(output_array.shape, (5,)) self.assertTrue((output_array == test_list).all()) def test_setting_array_from_none(self): foo = Foo() test_array = numpy.arange(5) self.assertIsNone(foo.maybe_array) foo.maybe_array = test_array self.assertIsInstance(foo.maybe_array, numpy.ndarray) foo.maybe_array = None self.assertIsNone(foo.maybe_array) def test_dtype(self): foo = Foo() foo.maybe_float_array = [1, 2, 3] array_value = foo.maybe_float_array self.assertIsInstance(array_value, numpy.ndarray) self.assertEqual(array_value.dtype, numpy.dtype(float)) def test_shape(self): foo = Foo() with self.assertRaises(TraitError): foo.maybe_two_d_array = [1, 2, 3] def test_change_notifications(self): foo = Foo() test_array = numpy.arange(-7, -2) different_test_array = numpy.arange(10) # Assigning None to something that's already None shouldn't fire. with self.assertTraitDoesNotChange(foo, 'maybe_array'): foo.maybe_array = None # Changing from None to an array: expect an event. with self.assertTraitChanges(foo, 'maybe_array'): foo.maybe_array = test_array # No event from assigning the same array again. with self.assertTraitDoesNotChange(foo, 'maybe_array'): foo.maybe_array = test_array # But assigning a new array fires an event. with self.assertTraitChanges(foo, 'maybe_array'): foo.maybe_array = different_test_array # No event even if the array is modified in place. different_test_array += 2 with self.assertTraitDoesNotChange(foo, 'maybe_array'): foo.maybe_array = different_test_array # Set back to None; we should get an event. with self.assertTraitChanges(foo, 'maybe_array'): foo.maybe_array = None def test_comparison_mode_override(self): foo = Foo() test_array = numpy.arange(-7, 2) with self.assertTraitChanges(foo, 'maybe_array_no_compare'): foo.maybe_array_no_compare = None with self.assertTraitChanges(foo, 'maybe_array_no_compare'): foo.maybe_array_no_compare = test_array with self.assertTraitChanges(foo, 'maybe_array_no_compare'): foo.maybe_array_no_compare = test_array def test_default_value_copied(self): # Check that we don't share defaults. test_default = numpy.arange(100.0, 110.0) class FooBar(HasTraits): foo = ArrayOrNone(value=test_default) bar = ArrayOrNone(value=test_default) foo_bar = FooBar() self.assertTrue((foo_bar.foo == test_default).all()) self.assertTrue((foo_bar.bar == test_default).all()) test_default += 2.0 self.assertFalse((foo_bar.foo == test_default).all()) self.assertFalse((foo_bar.bar == test_default).all()) foo = foo_bar.foo foo += 1729.0 self.assertFalse((foo_bar.foo == foo_bar.bar).all()) traits-4.6.0/traits/tests/test_automatic_adaptation.py000066400000000000000000000044421300633736300233120ustar00rootroot00000000000000############################################################################### # Copyright 2014 Enthought, Inc. ############################################################################### from traits.adaptation.api import reset_global_adaptation_manager from traits.api import HasTraits, Instance, List, register_factory, TraitError from traits.testing.unittest_tools import unittest class Foo(HasTraits): pass class Bar(HasTraits): pass def bar_to_foo_adapter(bar): return Foo() class FooContainer(HasTraits): not_adapting_foo = Instance(Foo) adapting_foo = Instance(Foo, adapt='yes') not_adapting_foo_list = List(Foo) adapting_foo_list = List(Instance(Foo, adapt='yes')) class TestAutomaticAdaptation(unittest.TestCase): #### 'TestCase' protocol ################################################## def setUp(self): reset_global_adaptation_manager() #### Tests ################################################################ def test_instance_trait_automatic_adaptation(self): bar = Bar() foo_container = FooContainer() # Before a Bar->Foo adapter is registered. with self.assertRaises(TraitError): foo_container.not_adapting_foo = bar with self.assertRaises(TraitError): foo_container.adapting_foo = bar # After a Bar->Foo adapter is registered. register_factory(bar_to_foo_adapter, Bar, Foo) with self.assertRaises(TraitError): foo_container.not_adapting_foo = bar foo_container.adapting_foo = bar self.assertIsInstance(foo_container.adapting_foo, Foo) def test_list_trait_automatic_adaptation(self): bar = Bar() foo_container = FooContainer() # Before a Bar->Foo adapter is registered. with self.assertRaises(TraitError): foo_container.not_adapting_foo_list = [bar] with self.assertRaises(TraitError): foo_container.adapting_foo_list = [bar] # After a Bar->Foo adapter is registered. register_factory(bar_to_foo_adapter, Bar, Foo) with self.assertRaises(TraitError): foo_container.not_adapting_foo_list = [bar] foo_container.adapting_foo_list = [bar] self.assertIsInstance(foo_container.adapting_foo_list[0], Foo) traits-4.6.0/traits/tests/test_bool.py000066400000000000000000000055151300633736300200550ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # # Copyright (c) 2016, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # ----------------------------------------------------------------------------- """ Tests for the Bool trait type. """ try: import numpy except ImportError: numpy_available = False else: numpy_available = True from traits.testing.unittest_tools import unittest from ..api import Bool, Dict, HasTraits, Int, TraitError class A(HasTraits): foo = Bool class TestBool(unittest.TestCase): def test_default_value(self): a = A() # We should get something of exact type bool. self.assertEqual(type(a.foo), bool) self.assertFalse(a.foo) def test_accepts_bool(self): a = A() a.foo = True self.assertTrue(a.foo) a.foo = False self.assertFalse(a.foo) def test_does_not_accept_int_or_float(self): a = A() bad_values = [-1, 1L, "a string", 1.0] for bad_value in bad_values: with self.assertRaises(TraitError): a.foo = bad_value # Double check that foo didn't actually change self.assertEqual(type(a.foo), bool) self.assertFalse(a.foo) @unittest.skipUnless(numpy_available, "numpy not available") def test_accepts_numpy_bool(self): # A bool trait should accept a NumPy bool_. a = A() a.foo = numpy.bool_(True) self.assertTrue(a.foo) @unittest.skipUnless(numpy_available, "numpy not available") def test_numpy_bool_retrieved_as_bool(self): a = A() a.foo = numpy.bool_(True) self.assertIsInstance(a.foo, bool) a.foo = numpy.bool_(False) self.assertIsInstance(a.foo, bool) @unittest.skipUnless(numpy_available, "numpy not available") def test_numpy_bool_accepted_as_dict_value(self): # Regression test for enthought/traits#299. class HasBoolDict(HasTraits): foo = Dict(Int, Bool) has_bool_dict = HasBoolDict() has_bool_dict.foo[1] = numpy.bool_(True) self.assertTrue(has_bool_dict.foo[1]) @unittest.skipUnless(numpy_available, "numpy not available") def test_numpy_bool_accepted_as_dict_key(self): # Regression test for enthought/traits#299. class HasBoolDict(HasTraits): foo = Dict(Bool, Int) has_bool_dict = HasBoolDict() key = numpy.bool_(True) has_bool_dict.foo[key] = 1 self.assertEqual(has_bool_dict.foo[key], 1) traits-4.6.0/traits/tests/test_category.py000066400000000000000000000071101300633736300207300ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: David C. Morrill # Description: #------------------------------------------------------------------------------ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Category, Str class Base(HasTraits): y = Str("Base y") z = Str("Base z") class BaseExtra(Category, Base): x = Str("BaseExtra x") class BasePlus(Category, Base): p = Str("BasePlus p") # z = Str("BasePlus z") overrides not allowed. class BasePlusPlus(BasePlus): pp = Str("BasePlusPlus pp") class CategoryTestCase(unittest.TestCase): """ Test cases for traits category """ def setUp(self): self.base = Base() return def test_base_category(self): """ Base class with traits """ self.assertEqual(self.base.y, "Base y", msg="y != 'Base y'") self.assertEqual(self.base.z, "Base z", msg="z != 'Base z'") return def test_extra_extension_category(self): """ Base class extended with a category subclass """ self.assertEqual(self.base.x, "BaseExtra x", msg="x != 'BaseExtra x'") return def test_plus_extension_category(self): """ Base class extended with two category subclasses """ self.assertEqual(self.base.x, "BaseExtra x", msg="x != 'BaseExtra x'") self.assertEqual(self.base.p, "BasePlus p", msg="p != 'BasePlus p'") return def test_subclass_extension_category(self): """ Category subclass does not extend base class. This test demonstrates that traits allows subclassing of a category class, but that the traits from the subclass are not actually added to the base class of the Category. Seems like the declaration of the subclass (BasePlusPlus) should fail. """ try: x = self.base.pp self.fail(msg="base.pp should have thrown AttributeError " "as Category subclassing is not supported.") except AttributeError: pass basepp = BasePlusPlus() return def test_subclass_instance_category(self): """ Category subclass instantiation not supported. This test demonstrates that traits allows subclassing of a category class, that subclass can be instantiated, but the traits of the parent class are not inherited. Seems like the declaration of the subclass (BasePlusPlus) should fail. """ bpp = BasePlusPlus() self.assertEqual(bpp.pp, "BasePlusPlus pp", msg="pp != 'BasePlusPlus pp'") try: self.assertEqual(bpp.p, "BasePlus p", msg="p != 'BasePlus p'") self.fail(msg="bpp.p should have thrown SystemError as " "instantiating a subclass of a category is not " "supported.") except SystemError: pass return # # support running this test individually, from the command-line as a script # if __name__ == '__main__': unittest.main() #### EOF ###################################################################### traits-4.6.0/traits/tests/test_class_traits.py000066400000000000000000000025231300633736300216110ustar00rootroot00000000000000""" Unit tests for the `HasTraits.class_traits` class function. """ from __future__ import absolute_import from traits import _py2to3 from traits.testing.unittest_tools import unittest from ..api import HasTraits, Int, List, Str class A(HasTraits): x = Int name = Str(marked=True) class B(A): pass class C(B): lst = List(marked=False) y = Int(marked=True) class TestClassTraits(unittest.TestCase): def test_all_class_traits(self): expected = ['x', 'name', 'trait_added', 'trait_modified'] _py2to3.assertCountEqual(self, A.class_traits(), expected) # Check that derived classes report the correct traits. _py2to3.assertCountEqual(self, B.class_traits(), expected) expected.extend(('lst', 'y')) _py2to3.assertCountEqual(self, C.class_traits(), expected) def test_class_traits_with_metadata(self): # Retrieve all traits that have the `marked` metadata # attribute set to True. traits = C.class_traits(marked=True) _py2to3.assertCountEqual(self, traits.keys(), ('y', 'name')) # Retrieve all traits that have a `marked` metadata attribute, # regardless of its value. marked_traits = C.class_traits(marked=lambda attr: attr is not None) _py2to3.assertCountEqual(self, marked_traits, ('y', 'name', 'lst')) traits-4.6.0/traits/tests/test_clone.py000066400000000000000000000213301300633736300202130ustar00rootroot00000000000000# Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: David C. Morrill # Description: from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Instance, Str, Any, Property class Foo(HasTraits): s = Str class ClassWithAny(HasTraits): x = Property _x = Any def _get_x(self): return self._x def _set_x(self, x): self._x = x class ClassWithInstance(HasTraits): x = Property _x = Instance(Foo) def _get_x(self): return self._x def _set_x(self, x): self._x = x class ClassWithClassAttribute(HasTraits): name = 'class defined name' foo = Str class BazAny(HasTraits): other = Any class BarAny(HasTraits): other = Any class BazInstance(HasTraits): # A BarInstance owned by this object. other = Instance('BarInstance') # A Foo owned by this object and not referenced by others. unique = Instance(Foo) # A Foo owned by this object and referenced by others. shared = Instance(Foo) # A Foo not owned by this object, may or may not be shared with other # objects found via owned references (e.g. other.ref). For the tests, # ref will always reference a Foo that is not owned by any of the objects # reachable via owned references, and therefore, that Foo object should # not be cloned. ref = Instance(Foo, copy='ref') class BarInstance(HasTraits): # used as circular reference back to owning BazInstance # NOTE: Setting copy to 'ref' will mean that when BarInstance is cloned, # the 'other' trait will not be copied, and will still point to the # 'other' attribute of the original BarInstance. other = Instance('BazInstance', copy='ref') # A Foo owned by this object and not referenced by others. unique = Instance(Foo) # A Foo owned by the 'other' object and referenced by this object. shared = Instance(Foo) # A Foo not owned by this object, may or may not be shared with other # objects found via owned references (e.g. other.ref). For the tests, # ref will always reference a Foo that is not owned by any of the objects # reachable via owned references, and therefore, that Foo object should # not be cloned. ref = Instance(Foo, copy='ref') class CloneTestCase(unittest.TestCase): """ Test cases for traits clone """ def test_any(self): b = ClassWithAny() f = Foo() f.s = 'the f' b.x = f bc = b.clone_traits(traits='all', copy='deep') self.assertNotEqual(id(bc.x), id(f), 'Foo x not cloned') return def test_instance(self): b = ClassWithInstance() f = Foo() f.s = 'the f' b.x = f bc = b.clone_traits(traits='all', copy='deep') self.assertNotEqual(id(bc.x), id(f), 'Foo x not cloned') return def test_class_attribute_missing(self): """ This test demonstrates a problem with Traits objects with class attributes. A change to the value of a class attribute via one instance causes the attribute to be removed from other instances. AttributeError: 'ClassWithClassAttribute' object has no attribute 'name' """ s = 'class defined name' c = ClassWithClassAttribute() self.assertEqual(s, c.name) c2 = ClassWithClassAttribute() self.assertEqual(s, c.name) self.assertEqual(s, c2.name) s2 = 'name class attribute changed via clone' c2.name = s2 self.assertEqual(s2, c2.name) # this is failing with AttributeError: 'ClassWithClassAttribute' # object has no attribute 'name' self.assertEqual(s, c.name) return def test_Any_circular_references(self): # Demonstrates that Any traits default to copy='ref' bar = BarAny() baz = BazAny() bar.other = baz baz.other = bar bar_copy = bar.clone_traits() self.assertIsNot(bar_copy, bar) self.assertIs(bar_copy.other, baz) self.assertIs(bar_copy.other.other, bar) def test_Any_circular_references_deep(self): # Demonstrates that Any traits can be forced to deep copy. bar = BarAny() baz = BazAny() bar.other = baz baz.other = bar bar_copy = bar.clone_traits(copy='deep') self.assertIsNot(bar_copy, bar) self.assertIsNot(bar_copy.other, baz) self.assertIsNot(bar_copy.other.other, bar) self.assertIs(bar_copy.other.other, bar_copy) def test_Instance_circular_references(self): ref = Foo(s='ref') bar_unique = Foo(s='bar.foo') shared = Foo(s='shared') baz_unique = Foo(s='baz.unique') baz = BazInstance() baz.unique = baz_unique baz.shared = shared baz.ref = ref bar = BarInstance() bar.unique = bar_unique bar.shared = shared bar.ref = ref bar.other = baz baz.other = bar baz_copy = baz.clone_traits() # Check Baz and Baz attributes.... self.assertIsNot(baz_copy, baz) self.assertIsNot(baz_copy.other, bar) self.assertIsNot(baz_copy.unique, baz.unique) self.assertIsNot(baz_copy.shared, baz.shared) self.assertIs(baz_copy.ref, ref) # Check Bar and Bar attributes.... bar_copy = baz_copy.other # Check the Bar owned object self.assertIsNot(bar_copy.unique, bar.unique) # Check the Bar reference to an object 'outside' the cloned graph. self.assertIs(bar_copy.ref, ref) # Check references to objects that where cloned, they should reference # the new clones not the original objects, except when copy is set # to 'ref' (as in the case of the 'other' trait). # When copy is set to ref, the trait does not get cloned. Therefore, # baz_copy.other.other is baz (and not baz_copy). self.assertIsNot(bar_copy.other, baz_copy) self.assertIs(bar_copy.other, baz) # 'shared' does not have copy set to 'ref', and so bar_copy.shared # should reference the new clone. # should reference the new clones self.assertIsNot(bar_copy.shared, baz.shared) self.assertIs(bar_copy.shared, baz_copy.shared) def test_Instance_circular_references_deep(self): ref = Foo(s='ref') bar_unique = Foo(s='bar.foo') shared = Foo(s='shared') baz_unique = Foo(s='baz.unique') baz = BazInstance() baz.unique = baz_unique baz.shared = shared baz.ref = ref bar = BarInstance() bar.unique = bar_unique bar.shared = shared bar.ref = ref bar.other = baz baz.other = bar baz_copy = baz.clone_traits(copy='deep') # Check Baz and Baz attributes.... self.assertIsNot(baz_copy, baz) self.assertIsNot(baz_copy.other, bar) self.assertIsNot(baz_copy.unique, baz.unique) self.assertIsNot(baz_copy.shared, baz.shared) # baz_copy.ref is checked below with bar_copy.ref. # Check Bar and Bar attributes.... bar_copy = baz_copy.other # Check the Bar owned object self.assertIsNot(bar_copy.unique, bar.unique) # Since the two original 'ref' links were to a shared object, # the cloned links should be to a shared object. Also, the shared # object should be the original 'ref' object, since copy was set to # 'ref'. self.assertIs(baz_copy.ref, bar_copy.ref) self.assertIs(bar_copy.ref,ref) # Check references to objects that where cloned, they should reference # the new clones not the original objects, except when copy is set # to 'ref' (as in the case of the 'other' trait). That is, the 'deep' # flag on clone_traits should not override the 'copy' metadata on # the trait. self.assertIsNot(bar_copy.other, baz_copy) self.assertIs(bar_copy.other, baz) # 'shared' does not have copy set to 'ref', and so bar_copy.shared # should reference the new clone. self.assertIsNot(bar_copy.shared, baz.shared) self.assertIs(bar_copy.shared, baz_copy.shared) # # support running this test individually, from the command-line as a script # if __name__ == '__main__': unittest.main() #### EOF ###################################################################### traits-4.6.0/traits/tests/test_container_events.py000066400000000000000000000120571300633736300224670ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Tests for Dict items_changed events """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Dict class MyClass(HasTraits): """ A dummy HasTraits class with a Dict """ d = Dict({"a": "apple", "b": "banana", "c": "cherry", "d": "durian"}) def __init__(self, callback): "The callback is called with the TraitDictEvent instance" self.callback = callback return def _d_items_changed(self, event): if self.callback: self.callback(event) return class MyOtherClass(HasTraits): """ A dummy HasTraits class with a Dict """ d = Dict({"a": "apple", "b": "banana", "c": "cherry", "d": "durian"}) class Callback: """ A stateful callback that gets initialized with the values to check for """ def __init__(self, obj, added={}, changed={}, removed={}): self.obj = obj self.added = added self.changed = changed self.removed = removed self.called = False return def __call__(self, event): if event.added != self.added: print "\n\n******Error\nevent.added:", event.added else: self.obj.assertEqual(event.added, self.added) self.obj.assertEqual(event.changed, self.changed) self.obj.assertEqual(event.removed, self.removed) self.called = True return class DictEventTestCase(unittest.TestCase): def test_setitem(self): # overwriting an existing item cb = Callback(self, changed={"c": "cherry"}) foo = MyClass(cb) foo.d["c"] = "coconut" self.assertTrue(cb.called) # adding a new item cb = Callback(self, added={"g": "guava"}) bar = MyClass(cb) bar.d["g"] = "guava" self.assertTrue(cb.called) return def test_delitem(self): cb = Callback(self, removed={"b": "banana"}) foo = MyClass(cb) del foo.d["b"] self.assertTrue(cb.called) return def test_clear(self): removed = MyClass(None).d.copy() cb = Callback(self, removed=removed) foo = MyClass(cb) foo.d.clear() self.assertTrue(cb.called) return def test_update(self): update_dict = {"a": "artichoke", "f": "fig"} cb = Callback(self, changed={"a": "apple"}, added={"f": "fig"}) foo = MyClass(cb) foo.d.update(update_dict) self.assertTrue(cb.called) return def test_setdefault(self): # Test retrieving an existing value cb = Callback(self) foo = MyClass(cb) self.assertEqual(foo.d.setdefault("a", "dummy"), "apple") self.assertFalse(cb.called) # Test adding a new value cb = Callback(self, added={"f": "fig"}) bar = MyClass(cb) self.assertTrue(bar.d.setdefault("f", "fig") == "fig") self.assertTrue(cb.called) return def test_pop(self): # Test popping a non-existent key cb = Callback(self) foo = MyClass(cb) self.assertEqual(foo.d.pop("x", "dummy"), "dummy") self.assertFalse(cb.called) # Test popping a regular item cb = Callback(self, removed={"c": "cherry"}) bar = MyClass(cb) self.assertEqual(bar.d.pop("c"), "cherry") self.assertTrue(cb.called) return def test_popitem(self): foo = MyClass(None) foo.d.clear() foo.d["x"] = "xylophone" cb = Callback(self, removed={"x": "xylophone"}) foo.callback = cb self.assertEqual(foo.d.popitem(), ("x", "xylophone")) self.assertTrue(cb.called) return def test_dynamic_listener(self): foo = MyOtherClass() # Test adding func = Callback(self, added={"g": "guava"}) foo.on_trait_change(func.__call__, "d_items") foo.d["g"] = "guava" foo.on_trait_change(func.__call__, "d_items", remove=True) self.assertTrue(func.called) # Test removing func2 = Callback(self, removed={"a": "apple"}) foo.on_trait_change(func2.__call__, "d_items") del foo.d["a"] foo.on_trait_change(func2.__call__, "d_items", remove=True) self.assertTrue(func2.called) # Test changing func3 = Callback(self, changed={"b": "banana"}) foo.on_trait_change(func3.__call__, "d_items") foo.d["b"] = "broccoli" foo.on_trait_change(func3.__call__, "d_items", remove=True) self.assertTrue(func3.called) return traits-4.6.0/traits/tests/test_copy_traits.py000066400000000000000000000230721300633736300214600ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Instance, Str class Shared(HasTraits): s = Str('new instance of Shared') class Foo(HasTraits): s = Str('new instance of Foo') shared = Instance(Shared) class Bar(HasTraits): s = Str('new instance of Bar') foo = Instance(Foo) shared = Instance(Shared) class Baz(HasTraits): s = Str('new instance of Baz') bar = Instance(Bar) shared = Instance(Shared) class CopyTraitsBase(unittest.TestCase): """ Validate that copy_traits """ __test__ = False def setUp(self): super(CopyTraitsBase, self).setUp() self.shared = Shared(s='shared') self.foo = Foo(shared=self.shared, s='foo') self.bar = Bar(shared=self.shared, foo=self.foo, s='bar') self.baz = Baz(shared=self.shared, bar=self.bar, s='baz') self.shared2 = Shared(s='shared2') self.foo2 = Foo(shared=self.shared2, s='foo2') self.bar2 = Bar(shared=self.shared2, foo=self.foo2, s='bar2') self.baz2 = Baz(shared=self.shared2, bar=self.bar2, s='baz2') return def set_shared_copy(self, value): """ Change the copy style for the 'shared' traits. """ self.foo.base_trait('shared').copy = value self.bar.base_trait('shared').copy = value self.baz.base_trait('shared').copy = value class TestCopyTraitsSetup(CopyTraitsBase): __test__ = True def setUp(self): super(TestCopyTraitsSetup, self).setUp() def test_setup(self): self.assertIs(self.foo, self.bar.foo) self.assertIs(self.bar, self.baz.bar) self.assertIs(self.foo.shared, self.shared) self.assertIs(self.bar.shared, self.shared) self.assertIs(self.baz.shared, self.shared) self.assertIs(self.foo2, self.bar2.foo) self.assertIs(self.bar2, self.baz2.bar) self.assertIs(self.foo2.shared, self.shared2) self.assertIs(self.bar2.shared, self.shared2) self.assertIs(self.baz2.shared, self.shared2) class CopyTraits(object): def test_baz2_s(self): self.assertEqual(self.baz2.s, 'baz') self.assertEqual(self.baz2.s, self.baz.s) def test_baz2_bar_s(self): self.assertEqual(self.baz2.bar.s, 'bar') self.assertEqual(self.baz2.bar.s, self.baz.bar.s) def test_baz2_bar_foo_s(self): self.assertEqual(self.baz2.bar.foo.s, 'foo') self.assertEqual(self.baz2.bar.foo.s, self.baz.bar.foo.s) def test_baz2_shared_s(self): self.assertEqual(self.baz2.shared.s, 'shared') self.assertEqual(self.baz2.bar.shared.s, 'shared') self.assertEqual(self.baz2.bar.foo.shared.s, 'shared') def test_baz2_bar(self): # First hand Instance trait is different and # is not the same object as the source. self.assertIsNot(self.baz2.bar, None) self.assertIsNot(self.baz2.bar, self.bar2) self.assertIsNot(self.baz2.bar, self.baz.bar) def test_baz2_bar_foo(self): # Second hand Instance trait is a different object and # is not the same object as the source. self.assertIsNot(self.baz2.bar.foo, None) self.assertIsNot(self.baz2.bar.foo, self.foo2) self.assertIsNot(self.baz2.bar.foo, self.baz.bar.foo) class CopyTraitsSharedCopyNone(object): def test_baz2_shared(self): # First hand Instance trait is a different object and # is not the same object as the source. self.assertIsNot(self.baz2.shared, None) self.assertIsNot(self.baz2.shared, self.shared2) self.assertIsNot(self.baz2.shared, self.shared) def test_baz2_bar_shared(self): # Second hand Instance that was shared is a different object and # not the same object as the source and # not the same object as the new first hand instance that was the same. # I.e. There are now (at least) two copies of one original object. self.assertIsNot(self.baz2.bar.shared, None) self.assertIsNot(self.baz2.bar.shared, self.shared2) self.assertIsNot(self.baz2.bar.shared, self.shared) self.assertIsNot(self.baz2.bar.shared, self.baz2.shared) def test_baz2_bar_foo_shared(self): # Third hand Instance that was shared is a different object and # not the same object as the source and # not the same object as the new first hand instance that was the same. # I.e. There are now (at least) two copies of one original object. self.assertIsNot(self.baz2.bar.foo.shared, None) self.assertIsNot(self.baz2.bar.foo.shared, self.shared2) self.assertIsNot(self.baz2.bar.foo.shared, self.shared) self.assertIsNot(self.baz2.bar.foo.shared, self.baz2.shared) def test_baz2_bar_and_foo_shared(self): # # THE BEHAVIOR DEMONSTRATED BY THIS TEST CASE DOES NOT SEEM TO BE # CORRECT. # # Second and Third hand Instance object that was shared with first hand # instance are the same as each other but # Every reference to the same original object has been replace by # a reference to the same copy of the same source object except the # first hand reference which is a different copy. # I.e. The shared relationship has been fubarred by copy_traits: it's # not maintained, but not completely destroyed. self.assertIs(self.baz2.bar.shared, self.baz2.bar.foo.shared) self.assertIsNot(self.baz2.shared, self.baz2.bar.foo.shared) class TestCopyTraitsSharedCopyNone(CopyTraits, CopyTraitsSharedCopyNone): __test__ = False def setUp(self): #super(TestCopyTraitsSharedCopyNone,self).setUp() # deep is the default value for Instance trait copy self.set_shared_copy('deep') return class TestCopyTraitsCopyNotSpecified(CopyTraitsBase, TestCopyTraitsSharedCopyNone): __test__ = True def setUp(self): # super(TestCopyTraitsCopyNotSpecified,self).setUp() CopyTraitsBase.setUp(self) TestCopyTraitsSharedCopyNone.setUp(self) self.baz2.copy_traits(self.baz) return class TestCopyTraitsCopyShallow(CopyTraitsBase, TestCopyTraitsSharedCopyNone): __test__ = True def setUp(self): # super(TestCopyTraitsCopyShallow,self).setUp() CopyTraitsBase.setUp(self) TestCopyTraitsSharedCopyNone.setUp(self) self.baz2.copy_traits(self.baz, copy='shallow') return class TestCopyTraitsCopyDeep(CopyTraitsBase, TestCopyTraitsSharedCopyNone): __test__ = True def setUp(self): # super(TestCopyTraitsCopyDeep,self).setUp() CopyTraitsBase.setUp(self) TestCopyTraitsSharedCopyNone.setUp(self) self.baz2.copy_traits(self.baz, copy='deep') return class CopyTraitsSharedCopyRef(object): def test_baz2_shared(self): # First hand Instance trait is a different object and # is the same object as the source. self.assertIsNot(self.baz2.shared, None) self.assertIsNot(self.baz2.shared, self.shared2) self.assertIs(self.baz2.shared, self.shared) def test_baz2_bar_shared(self): self.assertIsNot(self.baz2.bar.shared, None) self.assertIsNot(self.baz2.bar.shared, self.shared2) self.assertIs(self.baz2.bar.shared, self.shared) self.assertIs(self.baz2.bar.shared, self.baz2.shared) def test_baz2_bar_foo_shared(self): self.assertIsNot(self.baz2.bar.foo.shared, None) self.assertIsNot(self.baz2.bar.foo.shared, self.shared2) self.assertIs(self.baz2.bar.foo.shared, self.shared) self.assertIs(self.baz2.bar.foo.shared, self.baz2.shared) def test_baz2_bar_and_foo_shared(self): self.assertIs(self.baz2.bar.shared, self.baz2.bar.foo.shared) self.assertIs(self.baz2.shared, self.baz2.bar.foo.shared) class TestCopyTraitsSharedCopyRef(CopyTraits, CopyTraitsSharedCopyRef): __test__ = False def setUp(self): #super(TestCopyTraitsSharedCopyRef,self).setUp() self.set_shared_copy('ref') return # The next three tests demonstrate that a 'ref' trait is always copied as a # reference regardless of the copy argument to copy_traits. That is, shallow # and deep are indistinguishable. class TestCopyTraitsCopyNotSpecifiedSharedRef(CopyTraitsBase, TestCopyTraitsSharedCopyRef): __test__ = True def setUp(self): CopyTraitsBase.setUp(self) TestCopyTraitsSharedCopyRef.setUp(self) self.baz2.copy_traits(self.baz) return class TestCopyTraitsCopyShallowSharedRef(CopyTraitsBase, TestCopyTraitsSharedCopyRef): __test__ = True def setUp(self): CopyTraitsBase.setUp(self) TestCopyTraitsSharedCopyRef.setUp(self) self.baz2.copy_traits(self.baz, copy='shallow') return class TestCopyTraitsCopyDeepSharedRef(CopyTraitsBase, TestCopyTraitsSharedCopyRef): __test__ = True def setUp(self): CopyTraitsBase.setUp(self) TestCopyTraitsSharedCopyRef.setUp(self) self.baz2.copy_traits(self.baz, copy='deep') return ### EOF traits-4.6.0/traits/tests/test_copyable_trait_names.py000066400000000000000000000065671300633736300233160ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import (HasTraits, Any, Bool, Delegate, Event, Instance, Property, Str) class Foo(HasTraits): a = Any b = Bool s = Str i = Instance(HasTraits) e = Event d = Delegate('i') p = Property def _get_p(self): return self._p def _set_p(self, p): self._p = p # Read Only Property p_ro = Property def _get_p_ro(self): return id(self) # Write-only property p_wo = Property def _set_p_wo(self, p_wo): self._p_wo = p_wo class TestCopyableTraitNames(unittest.TestCase): """ Validate that copyable_trait_names returns the appropriate result. """ def setUp(self): foo = Foo() self.names = foo.copyable_trait_names() def test_events_not_copyable(self): self.assertNotIn('e', self.names) def test_read_only_property_not_copyable(self): self.assertNotIn('p_ro', self.names) def test_write_only_property_not_copyable(self): self.assertNotIn('p_wo', self.names) def test_any_copyable(self): self.assertIn('a', self.names) def test_bool_copyable(self): self.assertIn('b', self.names) def test_str_copyable(self): self.assertIn('s', self.names) def test_instance_copyable(self): self.assertIn('i', self.names) def test_delegate_copyable(self): self.assertIn('d', self.names) def test_property_copyable(self): self.assertIn('p', self.names) class TestCopyableTraitNameQueries(unittest.TestCase): def setUp(self): self.foo = Foo() def test_type_query(self): names = self.foo.copyable_trait_names(**{ 'type': 'trait' }) self.assertEqual(['a', 'b', 'i', 's'], sorted(names)) names = self.foo.copyable_trait_names(**{ 'type': lambda t: t in ('trait', 'property',) }) self.assertEqual(['a', 'b', 'i', 'p', 's'], sorted(names)) def test_property_query(self): names = self.foo.copyable_trait_names(**{ 'property': lambda p: p() and p()[1].__name__ == '_set_p', }) self.assertEqual(['p'], names) def test_unmodified_query(self): names = self.foo.copyable_trait_names(**{ 'is_trait_type': lambda f: f(Str) }) self.assertEqual(['s'], names) def test_queries_not_combined(self): """ Verify that metadata is not merged with metadata to find the copyable traits. """ eval_true = lambda x: True names = self.foo.copyable_trait_names(property=eval_true, type=eval_true, transient=eval_true) self.assertEqual(['a', 'b', 'd', 'e', 'i', 'p', 'p_ro', 'p_wo', 's', 'trait_added', 'trait_modified' ], sorted(names)) ### EOF traits-4.6.0/traits/tests/test_cythonized_traits.py000066400000000000000000000155621300633736300226730ustar00rootroot00000000000000""" Test some usage of Trait classes when the code is cythonized. The tests reflects some of the patterns needed in different applications. They probably don't cover all of the user case. Each test case is written as if the test code was in a separate module then compiled with Cython Inline before evaluation the produced object behaves properly. The tests need a Cython version > 0.19 and a compiler. """ try: import cython no_cython = False except ImportError: no_cython = True from ..testing.unittest_tools import unittest, UnittestTools def has_no_compiler(): if no_cython: return True # Easy way to check if we have access to a compiler code = "return 1+1" try: cython.inline(code) return False except: return True def cython_version(): if no_cython: return None from Cython.Compiler.Version import version return tuple(int(v) for v in version.split('.')) SKIP_TEST = has_no_compiler() # Cython 0.19 implementation of safe_type fails while parsing some of the # code. We provide a very basic implementation that always returns object # (we don't need any particular optimizations) def _always_object_type(arg, context): return 'object' class CythonizedTraitsTestCase(unittest.TestCase, UnittestTools): @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_simple_default_methods(self): code = """ from traits.api import HasTraits, Str class Test(HasTraits): name = Str def _name_default(self): return 'Joe' return Test() """ obj = cython.inline(code) self.assertEqual(obj.name, 'Joe') @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_basic_events(self): code = """ from traits.api import HasTraits, Str class Test(HasTraits): name = Str return Test() """ obj = cython.inline(code) with self.assertTraitChanges(obj, 'name', count=1): obj.name = 'changing_name' @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_static_handlers(self): code = """ from traits.api import HasTraits, Str, Int class Test(HasTraits): name = Str value = Int def _name_changed(self): self.value += 1 return Test() """ obj = cython.inline(code, get_type=_always_object_type, force=True) with self.assertTraitChanges(obj, 'value', count=1): obj.name = 'changing_name' self.assertEqual(obj.value, 1) @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_on_trait_change_decorator(self): code = """ from traits.api import HasTraits, Str, Int, on_trait_change class Test(HasTraits): name = Str value = Int @on_trait_change('name') def _update_value(self): self.value += 1 return Test() """ obj = cython.inline(code, get_type=_always_object_type, force=True, locals={}, globals={}) with self.assertTraitChanges(obj, 'value', count=1): obj.name = 'changing_name' self.assertEqual(obj.value, 1) @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_properties(self): code = """ from traits.api import HasTraits, Str, Int, Property, cached_property class Test(HasTraits): name = Str name_len = Property(depends_on='name') @cached_property def _get_name_len(self): return len(self.name) return Test() """ obj = cython.inline(code, get_type=_always_object_type, force=True, locals={}, globals={}) self.assertEqual(obj.name_len, len(obj.name)) # Assert dependency works obj.name = 'Bob' self.assertEqual(obj.name_len, len(obj.name)) @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_properties_with_standard_getter(self): code = """ from traits.api import HasTraits, Str, Int, Property class Test(HasTraits): name = Str def _get_name_length(self): return len(self.name) name_len = Property(_get_name_length) return Test() """ obj = cython.inline(code, get_type=_always_object_type, force=True, locals={}, globals={}) self.assertEqual(obj.name_len, len(obj.name)) # Assert dependency works obj.name = 'Bob' self.assertEqual(obj.name_len, len(obj.name)) @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_aliasing(self): code = """ from traits.api import HasTraits, Str, Int, Property def Alias(name): def _get_value(self): return getattr(self, name) def _set_value(self, value): return setattr(self, name, value) return Property(_get_value, _set_value) class Test(HasTraits): name = Str funky_name = Alias('name') return Test() """ obj = cython.inline(code, get_type=_always_object_type, force=True, locals={}, globals={}) self.assertEqual(obj.funky_name, obj.name) # Assert dependency works obj.name = 'Bob' self.assertEqual(obj.funky_name, obj.name) @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_aliasing_different_scope(self): code = """ from traits.api import HasTraits, Str, Int, Property def _get_value(self, name): return getattr(self, 'name') def _set_value(self, name, value): return setattr(self, 'name', value) class Test(HasTraits): name = Str funky_name = Property(_get_value, _set_value) return Test() """ obj = cython.inline(code, get_type=_always_object_type) self.assertEqual(obj.funky_name, obj.name) # Assert dependency works obj.name = 'Bob' self.assertEqual(obj.funky_name, obj.name) @unittest.skipIf(SKIP_TEST, 'Missing Cython and/or compiler') def test_on_trait_lambda_failure(self): # Lambda function are converted like builtins when cythonized which # causes the following code to fail code = """ from traits.api import HasTraits, Str, Int, Property def Alias(name): return Property( lambda obj: getattr(obj, name), lambda obj, value: setattr(obj, name, value) ) class Test(HasTraits): name = Str funky_name = Alias('name') return Test() """ try: cython.inline(code, get_type=_always_object_type, force=True, locals={}, globals={}) except: # We suppose we have an exception. Because of the usage of the # skipIf decorator on the test, we can't use an expectedFailure # decorator as they don't play well together. pass else: self.fail( 'Unexpected results. Cython was not managing lambda as regular' ' functions. Behaviour changed ...' ) traits-4.6.0/traits/tests/test_delegate.py000066400000000000000000000217721300633736300206770ustar00rootroot00000000000000# Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import Delegate, HasTraits, Instance, Str # global because event handlers are being called with wrong value for self baz_s_handler_self = None baz_sd_handler_self = None baz_u_handler_self = None baz_t_handler_self = None foo_s_handler_self = None foo_t_handler_self = None class Foo(HasTraits): s = Str('foo') t = Str('foo.t') u = Str('foo.u') def _s_changed(self, name, old, new): global foo_s_handler_self foo_s_handler_self = self return def _t_changed(self, name, old, new): global foo_t_handler_self foo_t_handler_self = self return class Bar(HasTraits): foo = Instance(Foo, ()) s = Delegate('foo') class BazModify(HasTraits): foo = Instance(Foo, ()) sd = Delegate('foo', prefix='s', modify=True) t = Delegate('foo', modify=True) u = Delegate('foo', listenable=False, modify=True) def _s_changed(self, name, old, new): # should never be called global baz_s_handler_self baz_s_handler_self = self return def _sd_changed(self, name, old, new): global baz_sd_handler_self baz_sd_handler_self = self return def _t_changed(self, name, old, new): global baz_t_handler_self baz_t_handler_self = self return def _u_changed(self, name, old, new): global baz_u_handler_self baz_u_handler_self = self return class BazNoModify(HasTraits): foo = Instance(Foo, ()) sd = Delegate('foo', prefix='s') t = Delegate('foo') u = Delegate('foo', listenable=False) def _s_changed(self, name, old, new): global baz_s_handler_self baz_s_handler_self = self return def _sd_changed(self, name, old, new): global baz_sd_handler_self baz_sd_handler_self = self return def _t_changed(self, name, old, new): global baz_t_handler_self baz_t_handler_self = self return def _u_changed(self, name, old, new): global baz_u_handler_self baz_u_handler_self = self return class DelegateTestCase(unittest.TestCase): """ Test cases for delegated traits. """ def setUp(self): """ Reset all of the globals. """ global baz_s_handler_self, baz_sd_handler_self, baz_u_handler_self global baz_t_handler_self, foo_s_handler_self, foo_t_handler_self baz_s_handler_self = None baz_sd_handler_self = None baz_u_handler_self = None baz_t_handler_self = None foo_s_handler_self = None foo_t_handler_self = None def test_reset(self): """ Test that a delegated trait may be reset. Deleting the attribute should reset the trait back to its initial delegation behavior. """ f = Foo() b = Bar(foo=f) # Check initial delegation. self.assertEqual(f.s, b.s) # Check that an override works. b.s = 'bar' self.assertNotEqual(f.s, b.s) # Check that we can reset back to delegation. This is what we are # really testing for. del b.s self.assertEqual(f.s, b.s) return # Below are 8 tests to check the calling of change notification handlers. # There are 8 cases for the 2x2x2 matrix with axes: # Delegate with prefix or not # Delegate with modify write through or not # Handler in the delegator and delegatee def test_modify_prefix_handler_on_delegator(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.s, b.sd) b.sd = 'changed' self.assertEqual(f.s, b.sd) # Don't expect _s_changed to be called because from Baz's perspective # the trait is named 'sd' self.assertEqual(baz_s_handler_self, None) # Do expect '_sd_changed' to be called with b as self self.assertEqual(baz_sd_handler_self, b) return def test_modify_prefix_handler_on_delegatee(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.s, b.sd) b.sd = 'changed' self.assertEqual(f.s, b.sd) # Foo expects its '_s_changed' handler to be called with f as self self.assertEqual(foo_s_handler_self, f) return def test_no_modify_prefix_handler_on_delegator(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.s, b.sd) b.sd = 'changed' self.assertNotEqual(f.s, b.sd) # Don't expect _s_changed to be called because from Baz's perspective # the trait is named 'sd' self.assertEqual(baz_s_handler_self, None) # Do expect '_sd_changed' to be called with b as self self.assertEqual(baz_sd_handler_self, b) return def test_no_modify_prefix_handler_on_delegatee_not_called(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.s, b.sd) b.sd = 'changed' self.assertNotEqual(f.s, b.sd) # Foo expects its '_s_changed' handler to be called with f as self self.assertEqual(foo_s_handler_self, None) return def test_modify_handler_on_delegator(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.t, b.t) b.t = 'changed' self.assertEqual(f.t, b.t) # Do expect '_t_changed' to be called with b as self self.assertEqual(baz_t_handler_self, b) return def test_modify_handler_on_delegatee(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.t, b.t) b.t = 'changed' self.assertEqual(f.t, b.t) # Foo t did change so '_t_changed' handler should be called self.assertEqual(foo_t_handler_self, f) return def test_no_modify_handler_on_delegator(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.t, b.t) b.t = 'changed' self.assertNotEqual(f.t, b.t) # Do expect '_t_changed' to be called with b as self self.assertEqual(baz_t_handler_self, b) return def test_no_modify_handler_on_delegatee_not_called(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.t, b.t) b.t = 'changed' self.assertNotEqual(f.t, b.t) # Foo t did not change so '_t_changed' handler should not be called self.assertEqual(foo_t_handler_self, None) return # Below are 4 tests for notification when the delegated trait is changed # directly rather than through the delegator. def test_no_modify_handler_on_delegatee_direct_change(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.t, b.t) f.t = 'changed' self.assertEqual(f.t, b.t) # Foo t did change so '_t_changed' handler should be called self.assertEqual(foo_t_handler_self, f) return def test_no_modify_handler_on_delegator_direct_change(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.t, b.t) f.t = 'changed' self.assertEqual(f.t, b.t) # Do expect '_t_changed' to be called with b as self self.assertEqual(baz_t_handler_self, b) return def test_modify_handler_on_delegatee_direct_change(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.t, b.t) f.t = 'changed' self.assertEqual(f.t, b.t) # Foo t did change so '_t_changed' handler should be called self.assertEqual(foo_t_handler_self, f) return def test_modify_handler_on_delegator_direct_change(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.t, b.t) f.t = 'changed' self.assertEqual(f.t, b.t) # Do expect '_t_changed' to be called with b as self self.assertEqual(baz_t_handler_self, b) return # Below are tests which check that we can turn off listenableness. def test_modify_handler_not_listenable(self): f = Foo() b = BazModify(foo=f) self.assertEqual(f.u, b.u) f.u = 'changed' self.assertEqual(f.u, b.u) # Do not expect '_u_changed' to be called. self.assertEqual(baz_u_handler_self, None) return def test_no_modify_handler_not_listenable(self): f = Foo() b = BazNoModify(foo=f) self.assertEqual(f.u, b.u) f.u = 'changed' self.assertEqual(f.u, b.u) # Do not expect '_u_changed' to be called. self.assertEqual(baz_u_handler_self, None) return if __name__ == '__main__': unittest.main() # EOF # traits-4.6.0/traits/tests/test_dict.py000066400000000000000000000074071300633736300200470ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt """ Test cases for dictionary (Dict) traits. """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..trait_types import Dict, Event, Str, TraitDictObject from ..has_traits import HasTraits, on_trait_change from ..trait_errors import TraitError # fixme: We'd like to use a callable instance for the listener so that we # can maintain state, but traits barfs trying to determine the signature 8^() def create_listener(): """ Create a listener for testing trait notifications. """ def listener(obj, trait_name, old, new): listener.obj = obj listener.trait_name = trait_name listener.new = new listener.old = old listener.called += 1 return listener.initialize = lambda: initialize_listener(listener) return initialize_listener(listener) def initialize_listener(listener): """ Initialize a listener so it looks like it hasn't been called. This allows us to re-use the listener without having to create and wire-up a new one. """ listener.obj = None listener.trait_name = None listener.old = None listener.new = None listener.called = 0 return listener # For convenience class TestDict(unittest.TestCase): """ Test cases for dictionary (Dict) traits. """ def test_modified_event(self): class Foo(HasTraits): name = Str modified = Event @on_trait_change('name') def _fire_modified_event(self): self.modified = True return class Bar(HasTraits): foos = Dict(Str, Foo) modified = Event @on_trait_change('foos_items,foos.modified') def _fire_modified_event(self, obj, trait_name, old, new): self.modified = True return bar = Bar() listener = create_listener() bar.on_trait_change(listener, 'modified') # Assign a completely new dictionary. bar.foos = {'dino': Foo(name='dino')} self.assertEqual(1, listener.called) self.assertEqual('modified', listener.trait_name) # Add an item to an existing dictionary. listener.initialize() fred = Foo(name='fred') bar.foos['fred'] = fred self.assertEqual(1, listener.called) self.assertEqual('modified', listener.trait_name) # Modify an item already in the dictionary. listener.initialize() fred.name = 'barney' self.assertEqual(1, listener.called) self.assertEqual('modified', listener.trait_name) # Overwrite an item in the dictionary. This is the one that fails! listener.initialize() bar.foos['fred'] = Foo(name='wilma') self.assertEqual(1, listener.called) self.assertEqual('modified', listener.trait_name) return def test_validate(self): """ Check the validation method. """ foo = Dict() # invalid value with self.assertRaises(TraitError): foo.validate(object=HasTraits(), name='bar', value=None) # valid value result = foo.validate(object=HasTraits(), name='bar', value={}) self.assertIsInstance(result, TraitDictObject) # object is None (check for issue #71) result = foo.validate(object=None, name='bar', value={}) self.assertEqual(result, {}) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_dynamic_notifiers.py000066400000000000000000000177611300633736300226360ustar00rootroot00000000000000""" Tests for the dynamic notifiers. """ import gc from traits import _py2to3 from traits.api import Event, Float, HasTraits, List, on_trait_change from traits.testing.unittest_tools import unittest from traits import trait_notifiers class DynamicNotifiers(HasTraits): ok = Float fail = Float priority_test = Event # Lists where we accumulate the arguments of calls to the traits notifiers. rebind_calls_0 = List rebind_calls_1 = List rebind_calls_2 = List rebind_calls_3 = List rebind_calls_4 = List exceptions_from = List prioritized_notifications = List #### 'ok' trait listeners @on_trait_change('ok') def method_listener_0(self): self.rebind_calls_0.append(True) @on_trait_change('ok') def method_listener_1(self, new): self.rebind_calls_1.append(new) @on_trait_change('ok') def method_listener_2(self, name, new): self.rebind_calls_2.append((name, new)) @on_trait_change('ok') def method_listener_3(self, obj, name, new): self.rebind_calls_3.append((obj, name, new)) @on_trait_change('ok') def method_listener_4(self, obj, name, old, new): self.rebind_calls_4.append((obj, name, old, new)) #### 'fail' trait listeners @on_trait_change('fail') def failing_method_listener_0(self): self.exceptions_from.append(0) raise Exception('error') @on_trait_change('fail') def failing_method_listener_1(self, new): self.exceptions_from.append(1) raise Exception('error') @on_trait_change('fail') def failing_method_listener_2(self, name, new): self.exceptions_from.append(2) raise Exception('error') @on_trait_change('fail') def failing_method_listener_3(self, obj, name, new): self.exceptions_from.append(3) raise Exception('error') @on_trait_change('fail') def failing_method_listener_4(self, obj, name, old, new): self.exceptions_from.append(4) raise Exception('error') def low_priority_first(self): self.prioritized_notifications.append(0) def high_priority_first(self): self.prioritized_notifications.append(1) def low_priority_second(self): self.prioritized_notifications.append(2) def high_priority_second(self): self.prioritized_notifications.append(3) # 'ok' function listeners calls_0 = [] def function_listener_0(): calls_0.append(True) calls_1 = [] def function_listener_1(new): calls_1.append(new) calls_2 = [] def function_listener_2(name, new): calls_2.append((name, new)) calls_3 = [] def function_listener_3(obj, name, new): calls_3.append((obj, name, new)) calls_4 = [] def function_listener_4(obj, name, old, new): calls_4.append((obj, name, old, new)) # 'fail' function listeners exceptions_from = [] def failing_function_listener_0(): exceptions_from.append(0) raise Exception('error') def failing_function_listener_1(new): exceptions_from.append(1) raise Exception('error') def failing_function_listener_2(name, new): exceptions_from.append(2) raise Exception('error') def failing_function_listener_3(obj, name, new): exceptions_from.append(3) raise Exception('error') def failing_function_listener_4(obj, name, old, new): exceptions_from.append(4) raise Exception('error') class TestDynamicNotifiers(unittest.TestCase): #### 'TestCase' protocol ################################################## def setUp(self): self.exceptions = [] trait_notifiers.push_exception_handler(self._handle_exception) def tearDown(self): trait_notifiers.pop_exception_handler() #### Private protocol ##################################################### def _handle_exception(self, obj, name, old, new): self.exceptions.append((obj, name, old, new)) #### Tests ################################################################ def test_dynamic_notifiers_methods(self): obj = DynamicNotifiers(ok=2) obj.ok = 3 self.assertEqual(len(obj.rebind_calls_0), 2) expected_1 = [2, 3] self.assertEqual(expected_1, obj.rebind_calls_1) expected_2 = [('ok', 2), ('ok', 3)] self.assertEqual(expected_2, obj.rebind_calls_2) expected_3 = [(obj, 'ok', 2), (obj, 'ok', 3)] self.assertEqual(expected_3, obj.rebind_calls_3) expected_4 = [(obj, 'ok', 0, 2), (obj, 'ok', 2, 3)] self.assertEqual(expected_4, obj.rebind_calls_4) def test_dynamic_notifiers_methods_failing(self): obj = DynamicNotifiers() obj.fail = 1 _py2to3.assertCountEqual(self, [0, 1, 2, 3, 4], obj.exceptions_from) self.assertEqual([(obj, 'fail', 0, 1)]*5, self.exceptions) def test_dynamic_notifiers_functions(self): obj = DynamicNotifiers() obj.on_trait_change(function_listener_0, 'ok') obj.on_trait_change(function_listener_1, 'ok') obj.on_trait_change(function_listener_2, 'ok') obj.on_trait_change(function_listener_3, 'ok') obj.on_trait_change(function_listener_4, 'ok') obj.ok = 2 obj.ok = 3 expected_1 = [2, 3] self.assertEqual(expected_1, calls_1) expected_2 = [('ok', 2), ('ok', 3)] self.assertEqual(expected_2, calls_2) expected_3 = [(obj, 'ok', 2), (obj, 'ok', 3)] self.assertEqual(expected_3, calls_3) expected_4 = [(obj, 'ok', 0, 2), (obj, 'ok', 2, 3)] self.assertEqual(expected_4, calls_4) def test_priority_notifiers_first(self): obj = DynamicNotifiers() expected_high = set([1, 3]) expected_low = set([0, 2]) obj.on_trait_change(obj.low_priority_first, 'priority_test') obj.on_trait_change(obj.high_priority_first, 'priority_test', priority=True) obj.on_trait_change(obj.low_priority_second, 'priority_test') obj.on_trait_change(obj.high_priority_second, 'priority_test', priority=True) obj.priority_test = None high = set(obj.prioritized_notifications[:2]) low = set(obj.prioritized_notifications[2:]) self.assertSetEqual(expected_high, high) self.assertSetEqual(expected_low, low) def test_dynamic_notifiers_functions_failing(self): obj = DynamicNotifiers() obj.on_trait_change(failing_function_listener_0, 'fail') obj.on_trait_change(failing_function_listener_1, 'fail') obj.on_trait_change(failing_function_listener_2, 'fail') obj.on_trait_change(failing_function_listener_3, 'fail') obj.on_trait_change(failing_function_listener_4, 'fail') obj.fail = 1 _py2to3.assertCountEqual(self, [0, 1, 2, 3, 4], obj.exceptions_from) # 10 failures: 5 are from the internal dynamic listeners, see # test_dynamic_notifiers_methods_failing self.assertEqual([(obj, 'fail', 0, 1)] * 10, self.exceptions) def test_object_can_be_garbage_collected(self): # Make sure that a trait object can be garbage collected even though # there are listener to its traits. import weakref obj = DynamicNotifiers() obj.on_trait_change(function_listener_0, 'ok') # Create a weak reference to `obj` with a callback that flags when the # object is finalized. obj_collected = [] def obj_collected_callback(weakref): obj_collected.append(True) obj_weakref = weakref.ref(obj, obj_collected_callback) # Remove reference to `obj`, and check that the weak reference # callback has been called, indicating that it has been collected. del obj self.assertEqual(obj_collected, [True]) def test_creating_notifiers_dont_create_cyclic_garbage(self): gc.collect() DynamicNotifiers() # When an object with dynamic listeners has no more references, # it should not create cyclic garbage. self.assertEqual(gc.collect(), 0) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_dynamic_trait_definition.py000066400000000000000000000021461300633736300241560ustar00rootroot00000000000000from traits.testing.unittest_tools import unittest from traits.api import Float, HasTraits, Int, List class Foo(HasTraits): x = Float y_changes = List def _y_changed(self, new): self.y_changes.append(new) class TestDynamicTraitDefinition(unittest.TestCase): """ Test demonstrating special change events using the 'event' metadata. """ def test_add_trait(self): foo = Foo(x=3) foo.add_trait('y', Int) self.assertTrue(hasattr(foo, 'y')) self.assertEqual(type(foo.y), int) foo.y = 4 self.assertEqual(foo.y_changes, [4]) def test_remove_trait(self): foo = Foo(x=3) # We can't remove a "statically" added trait (i.e., a trait defined # in the Foo class). result = foo.remove_trait('x') self.assertFalse(result) # We can remove dynamically added traits. foo.add_trait('y', Int) foo.y = 70 result = foo.remove_trait('y') self.assertTrue(result) self.assertFalse(hasattr(foo, 'y')) foo.y = 10 self.assertEqual(foo.y_changes, [70]) traits-4.6.0/traits/tests/test_enum.py000066400000000000000000000012621300633736300200610ustar00rootroot00000000000000from traits.testing.unittest_tools import unittest from traits.api import Enum, HasTraits, List, Property, TraitError class ExampleModel(HasTraits): valid_models = Property(List) root = Enum(values='valid_models') def _get_valid_models(self): return ['model1', 'model2', 'model3'] class EnumTestCase(unittest.TestCase): def test_valid_enum(self): example_model = ExampleModel(root='model1') example_model.root = 'model2' def test_invalid_enum(self): example_model = ExampleModel(root='model1') def assign_invalid(): example_model.root = 'not_valid_model' self.assertRaises(TraitError, assign_invalid) traits-4.6.0/traits/tests/test_event_order.py000066400000000000000000000054771300633736300214450ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This Software is provided without warranty under the terms of the # BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The # license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Str, Instance, Any class TestEventOrder(unittest.TestCase): """ Tests that demonstrate that trait events are delivered in LIFO order rather than FIFO order. Baz receives the "effect" event before it receives the "cause" event. """ def setUp(self): foo = Foo(cause='ORIGINAL') bar = Bar(foo=foo, test=self) baz = Baz(bar=bar, test=self) self.events_delivered = [] foo.cause = 'CHANGE' return def test_lifo_order(self): lifo = ['Bar._caused_changed', 'Baz._effect_changed', 'Baz._caused_changed'] self.assertEqual(self.events_delivered, lifo) return def test_not_fifo_order(self): fifo = ['Bar._caused_changed', 'Baz._caused_changed', 'Baz._effect_changed'] self.assertNotEqual(self.events_delivered, fifo) return class Foo(HasTraits): cause = Str class Bar(HasTraits): foo = Instance(Foo) effect = Str test = Any def _foo_changed(self, obj, old, new): if old is not None and old is not new: old.on_trait_change(self._cause_changed, name='cause', remove=True) if new is not None: new.on_trait_change(self._cause_changed, name='cause') return def _cause_changed(self, obj, name, old, new): self.test.events_delivered.append('Bar._caused_changed') self.effect = new.lower() return class Baz(HasTraits): bar = Instance(Bar) test = Any def _bar_changed(self, obj, old, new): if old is not None and old is not new: old.on_trait_change(self._effect_changed, name='effect', remove=True) old.foo.on_trait_change(self._cause_changed, name='cause', remove=True) if new is not None: new.foo.on_trait_change(self._cause_changed, name='cause') new.on_trait_change(self._effect_changed, name='effect') return def _cause_changed(self, obj, name, old, new): self.test.events_delivered.append('Baz._caused_changed') return def _effect_changed(self, obj, name, old, new): self.test.events_delivered.append('Baz._effect_changed') return ### EOF ####################################################################### traits-4.6.0/traits/tests/test_extended_notifiers.py000066400000000000000000000160451300633736300230040ustar00rootroot00000000000000""" Tests for the extended notifiers. The "extended notifiers" are set up internally when using extended traits, to add/remove traits listeners when one of the intermediate traits changes. For example, in a listener for the extended trait `a.b`, we need to add/remove listeners to `a:b` when `a` changes. """ from traits import _py2to3 from traits.api import Float, HasTraits, List from traits.testing.unittest_tools import unittest from traits import trait_notifiers class ExtendedNotifiers(HasTraits): def __init__(self, **traits): # Set up the 'extended' internal notifiers (see module docstring) ok_listeners = [self.method_listener_0, self.method_listener_1, self.method_listener_2, self.method_listener_3, self.method_listener_4] for listener in ok_listeners: self._on_trait_change(listener, 'ok', dispatch='extended') fail_listeners = [self.failing_method_listener_0, self.failing_method_listener_1, self.failing_method_listener_2, self.failing_method_listener_3, self.failing_method_listener_4] for listener in fail_listeners: self._on_trait_change(listener, 'fail', dispatch='extended') super(ExtendedNotifiers, self).__init__(**traits) ok = Float fail = Float # Lists where we accumulate the arguments of calls to the traits notifiers. rebind_calls_0 = List rebind_calls_1 = List rebind_calls_2 = List rebind_calls_3 = List rebind_calls_4 = List exceptions_from = List #### 'ok' trait listeners def method_listener_0(self): self.rebind_calls_0.append(True) def method_listener_1(self, new): self.rebind_calls_1.append(new) def method_listener_2(self, name, new): self.rebind_calls_2.append((name, new)) def method_listener_3(self, obj, name, new): self.rebind_calls_3.append((obj, name, new)) def method_listener_4(self, obj, name, old, new): self.rebind_calls_4.append((obj, name, old, new)) #### 'fail' trait listeners def failing_method_listener_0(self): self.exceptions_from.append(0) raise Exception('error') def failing_method_listener_1(self, new): self.exceptions_from.append(1) raise Exception('error') def failing_method_listener_2(self, name, new): self.exceptions_from.append(2) raise Exception('error') def failing_method_listener_3(self, obj, name, new): self.exceptions_from.append(3) raise Exception('error') def failing_method_listener_4(self, obj, name, old, new): self.exceptions_from.append(4) raise Exception('error') # 'ok' function listeners calls_0 = [] def function_listener_0(): calls_0.append(True) calls_1 = [] def function_listener_1(new): calls_1.append(new) calls_2 = [] def function_listener_2(name, new): calls_2.append((name, new)) calls_3 = [] def function_listener_3(obj, name, new): calls_3.append((obj, name, new)) calls_4 = [] def function_listener_4(obj, name, old, new): calls_4.append((obj, name, old, new)) # 'fail' function listeners exceptions_from = [] def failing_function_listener_0(): exceptions_from.append(0) raise Exception('error') def failing_function_listener_1(new): exceptions_from.append(1) raise Exception('error') def failing_function_listener_2(name, new): exceptions_from.append(2) raise Exception('error') def failing_function_listener_3(obj, name, new): exceptions_from.append(3) raise Exception('error') def failing_function_listener_4(obj, name, old, new): exceptions_from.append(4) raise Exception('error') class TestExtendedNotifiers(unittest.TestCase): #### 'TestCase' protocol ################################################## def setUp(self): self.exceptions = [] trait_notifiers.push_exception_handler(self._handle_exception) def tearDown(self): trait_notifiers.pop_exception_handler() #### Private protocol ##################################################### def _handle_exception(self, obj, name, old, new): self.exceptions.append((obj, name, old, new)) #### Tests ################################################################ def test_extended_notifiers_methods(self): obj = ExtendedNotifiers(ok=2) obj.ok = 3 self.assertEqual(len(obj.rebind_calls_0), 2) expected_1 = [2, 3] self.assertEqual(expected_1, obj.rebind_calls_1) expected_2 = [('ok', 2), ('ok', 3)] self.assertEqual(expected_2, obj.rebind_calls_2) expected_3 = [(obj, 'ok', 2), (obj, 'ok', 3)] self.assertEqual(expected_3, obj.rebind_calls_3) expected_4 = [(obj, 'ok', 0, 2), (obj, 'ok', 2, 3)] self.assertEqual(expected_4, obj.rebind_calls_4) def test_extended_notifiers_methods_failing(self): obj = ExtendedNotifiers() obj.fail = 1 _py2to3.assertCountEqual(self, [0, 1, 2, 3, 4], obj.exceptions_from) self.assertEqual([(obj, 'fail', 0, 1)]*5, self.exceptions) def test_extended_notifiers_functions(self): obj = ExtendedNotifiers() obj._on_trait_change(function_listener_0, 'ok', dispatch='extended') obj._on_trait_change(function_listener_1, 'ok', dispatch='extended') obj._on_trait_change(function_listener_2, 'ok', dispatch='extended') obj._on_trait_change(function_listener_3, 'ok', dispatch='extended') obj._on_trait_change(function_listener_4, 'ok', dispatch='extended') obj.ok = 2 obj.ok = 3 expected_1 = [2, 3] self.assertEqual(expected_1, calls_1) expected_2 = [('ok', 2), ('ok', 3)] self.assertEqual(expected_2, calls_2) expected_3 = [(obj, 'ok', 2), (obj, 'ok', 3)] self.assertEqual(expected_3, calls_3) expected_4 = [(obj, 'ok', 0, 2), (obj, 'ok', 2, 3)] self.assertEqual(expected_4, calls_4) def test_extended_notifiers_functions_failing(self): obj = ExtendedNotifiers() obj._on_trait_change(failing_function_listener_0, 'fail', dispatch='extended') obj._on_trait_change(failing_function_listener_1, 'fail', dispatch='extended') obj._on_trait_change(failing_function_listener_2, 'fail', dispatch='extended') obj._on_trait_change(failing_function_listener_3, 'fail', dispatch='extended') obj._on_trait_change(failing_function_listener_4, 'fail', dispatch='extended') obj.fail = 1 _py2to3.assertCountEqual(self, [0, 1, 2, 3, 4], obj.exceptions_from) # 10 failures: 5 are from the internal extended listeners, see # test_extended_notifiers_methods_failing self.assertEqual([(obj, 'fail', 0, 1)] * 10, self.exceptions) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_extended_trait_change.py000066400000000000000000000573441300633736300234410ustar00rootroot00000000000000# Unit test case for testing HasTraits 'on_trait_change' support. # # Written by: David C. Morrill # # Date: 4/10/2007 # # (c) Copyright 2007 by Enthought, Inc. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt """ Unit test case for testing HasTraits 'on_trait_change' support. """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import (Any, Dict, HasTraits, Instance, Int, List, Property, TraitDictEvent, TraitError, TraitListEvent, Undefined, cached_property, on_trait_change, pop_exception_handler, push_exception_handler) from ..trait_handlers import TraitListObject, TraitDictObject class ArgCheckBase(HasTraits): value = Int(0) int1 = Int(0, test=True) int2 = Int(0) int3 = Int(0, test=True) tint1 = Int(0) tint2 = Int(0, test=True) tint3 = Int(0) calls = Int(0) tc = Any class ArgCheckSimple(ArgCheckBase): def arg_check0(self): self.calls += 1 def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.value) def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, 'value') self.tc.assertEqual(new, self.value) def arg_check3(self, object, name, new): self.calls += 1 self.tc.assertIs(object, self) self.tc.assertEqual(name, 'value') self.tc.assertEqual(new, self.value) def arg_check4(self, object, name, old, new): self.calls += 1 self.tc.assertIs(object, self) self.tc.assertEqual(name, 'value') self.tc.assertEqual(old, (self.value - 1)) self.tc.assertEqual(new, self.value) class ArgCheckDecorator(ArgCheckBase): @on_trait_change('value') def arg_check0(self): self.calls += 1 @on_trait_change('value') def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.value) @on_trait_change('value') def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, 'value') self.tc.assertEqual(new, self.value) @on_trait_change('value') def arg_check3(self, object, name, new): self.calls += 1 self.tc.assertIs(object, self) self.tc.assertEqual(name, 'value') self.tc.assertEqual(new, self.value) @on_trait_change('value') def arg_check4(self, object, name, old, new): self.calls += 1 self.tc.assertIs(object, self) self.tc.assertEqual(name, 'value') self.tc.assertEqual(old, (self.value - 1)) self.tc.assertEqual(new, self.value) class Instance1(HasTraits): ref = Instance(ArgCheckBase, ()) calls = Int(0) exp_object = Any exp_name = Any dst_name = Any exp_old = Any exp_new = Any dst_new = Any tc = Any @on_trait_change('ref.value') def arg_check0(self): self.calls += 1 @on_trait_change('ref.value') def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.dst_new) @on_trait_change('ref.value') def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, self.dst_name) self.tc.assertEqual(new, self.dst_new) @on_trait_change('ref.value') def arg_check3(self, object, name, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) @on_trait_change('ref.value') def arg_check4(self, object, name, old, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(old, self.exp_old) self.tc.assertEqual(new, self.exp_new) class List1(HasTraits): refs = List(ArgCheckBase) calls = Int(0) exp_object = Any exp_name = Any type_old = Any exp_old = Any type_new = Any exp_new = Any tc = Any @on_trait_change('refs.value') def arg_check0(self): self.calls += 1 @on_trait_change('refs.value') def arg_check3(self, object, name, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) if self.type_new is None: self.tc.assertEqual(new, self.exp_new) else: self.tc.assertIsInstance(new, self.type_new) @on_trait_change('refs.value') def arg_check4(self, object, name, old, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) if self.type_old is None: self.tc.assertEqual(old, self.exp_old) else: self.tc.assertIsInstance(old, self.type_old) if self.type_new is None: self.tc.assertEqual(new, self.exp_new) else: self.tc.assertIsInstance(new, self.type_new) class List2(HasTraits): refs = List(ArgCheckBase) calls = Int(0) exp_new = Any tc = Any @on_trait_change('refs.value') def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.exp_new) class List3(HasTraits): refs = List(ArgCheckBase) calls = Int(0) exp_name = Any exp_new = Any tc = Any @on_trait_change('refs.value') def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) class Dict1(List1): refs = Dict(Int, ArgCheckBase) class Dict2(HasTraits): refs = Dict(Int, ArgCheckBase) calls = Int(0) exp_new = Any tc = Any @on_trait_change('refs.value') def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.exp_new) class Dict3(HasTraits): refs = Dict(Int, ArgCheckBase) calls = Int(0) exp_name = Any exp_new = Any tc = Any @on_trait_change('refs.value') def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) class Complex(HasTraits): int1 = Int(0, test=True) int2 = Int(0) int3 = Int(0, test=True) tint1 = Int(0) tint2 = Int(0, test=True) tint3 = Int(0) ref = Instance(ArgCheckBase, ()) calls = Int(0) exp_object = Any exp_name = Any dst_name = Any exp_old = Any exp_new = Any dst_new = Any tc = Any def arg_check0(self): self.calls += 1 def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.exp_new) def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) def arg_check3(self, object, name, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) def arg_check4(self, object, name, old, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(old, self.exp_old) self.tc.assertEqual(new, self.exp_new) class Link(HasTraits): next = Any prev = Any value = Int(0) class LinkTest(HasTraits): head = Instance(Link) calls = Int(0) exp_object = Any exp_name = Any dst_name = Any exp_old = Any exp_new = Any dst_new = Any tc = Any def arg_check0(self): self.calls += 1 def arg_check1(self, new): self.calls += 1 self.tc.assertEqual(new, self.exp_new) def arg_check2(self, name, new): self.calls += 1 self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) def arg_check3(self, object, name, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(new, self.exp_new) def arg_check4(self, object, name, old, new): self.calls += 1 self.tc.assertIs(object, self.exp_object) self.tc.assertEqual(name, self.exp_name) self.tc.assertEqual(old, self.exp_old) self.tc.assertEqual(new, self.exp_new) class PropertyDependsOn(HasTraits): sum = Property(depends_on='ref.[int1,int2,int3]') ref = Instance(ArgCheckBase, ()) pcalls = Int(0) calls = Int(0) exp_old = Any exp_new = Any tc = Any @cached_property def _get_sum(self): self.pcalls += 1 r = self.ref return (r.int1 + r.int2 + r.int3) def _sum_changed(self, old, new): self.calls += 1 self.tc.assertEqual(old, self.exp_old) self.tc.assertEqual(new, self.exp_new) class OnTraitChangeTest(unittest.TestCase): def setUp(self): def ignore(*args): pass push_exception_handler(handler=ignore, reraise_exceptions=True) def tearDown(self): pop_exception_handler() def test_arg_check_simple(self): ac = ArgCheckSimple(tc=self) ac.on_trait_change(ac.arg_check0, 'value') ac.on_trait_change(ac.arg_check1, 'value') ac.on_trait_change(ac.arg_check2, 'value') ac.on_trait_change(ac.arg_check3, 'value') ac.on_trait_change(ac.arg_check4, 'value') for i in range(3): ac.value += 1 self.assertEqual(ac.calls, (3 * 5)) ac.on_trait_change(ac.arg_check0, 'value', remove=True) ac.on_trait_change(ac.arg_check1, 'value', remove=True) ac.on_trait_change(ac.arg_check2, 'value', remove=True) ac.on_trait_change(ac.arg_check3, 'value', remove=True) ac.on_trait_change(ac.arg_check4, 'value', remove=True) for i in range(3): ac.value += 1 self.assertEqual(ac.calls, (3 * 5)) self.assertEqual(ac.value, (2 * 3)) def test_arg_check_decorator(self): ac = ArgCheckDecorator(tc=self) for i in range(3): ac.value += 1 self.assertEqual(ac.calls, (3 * 5)) self.assertEqual(ac.value, 3) def test_instance1(self): i1 = Instance1(tc=self) for i in range(3): i1.trait_set(exp_object=i1.ref, exp_name='value', dst_name='value', exp_old=i, exp_new=(i + 1), dst_new=(i + 1)) i1.ref.value = (i + 1) self.assertEqual(i1.calls, (3 * 5)) self.assertEqual(i1.ref.value, 3) ref = ArgCheckBase() i1.trait_set(exp_object=i1, exp_name='ref', dst_name='value', exp_old=i1.ref, exp_new=ref, dst_new=0) i1.ref = ref self.assertEqual(i1.calls, (4 * 5)) self.assertEqual(i1.ref.value, 0) for i in range(3): i1.trait_set(exp_object=i1.ref, exp_name='value', dst_name='value', exp_old=i, exp_new=(i + 1), dst_new=(i + 1)) i1.ref.value = (i + 1) self.assertEqual(i1.calls, (7 * 5)) self.assertEqual(i1.ref.value, 3) def test_list1(self): l1 = List1(tc=self) for i in range(3): ac = ArgCheckBase() l1.trait_set(exp_object=l1, exp_name='refs_items', type_old=None, exp_old=Undefined, type_new=TraitListEvent) l1.refs.append(ac) #self.assertEqual(l1.calls, (3 * 3)) # FIXME for i in range(3): self.assertEqual(l1.refs[i].value, 0) refs = [ArgCheckBase(), ArgCheckBase(), ArgCheckBase()] l1.trait_set(exp_object=l1, exp_name='refs', type_old=None, exp_old=l1.refs, type_new=TraitListObject) l1.refs = refs #self.assertEqual(l1.calls, (4 * 3)) for i in range(3): self.assertEqual(l1.refs[i].value, 0) for i in range(3): for j in range(3): l1.trait_set(exp_object=l1.refs[j], exp_name='value', type_old=None, exp_old=i, type_new=None, exp_new=(i + 1)) l1.refs[j].value = (i + 1) #self.assertEqual(l1.calls, (13 * 3)) for i in range(3): self.assertEqual(l1.refs[i].value, 3) def test_list2(self): self.check_list(List2(tc=self)) def test_list3(self): self.check_list(List3(tc=self)) def test_dict1(self): d1 = Dict1(tc=self) for i in range(3): ac = ArgCheckBase() d1.trait_set(exp_object=d1, exp_name='refs_items', type_old=None, exp_old=Undefined, type_new=TraitDictEvent) d1.refs[i] = ac #self.assertEqual(d1.calls, (3 * 3)) # FIXME for i in range(3): self.assertEqual(d1.refs[i].value, 0) refs = {0: ArgCheckBase(), 1: ArgCheckBase(), 2: ArgCheckBase()} d1.trait_set(exp_object=d1, exp_name='refs', type_old=None, exp_old=d1.refs, type_new=TraitDictObject) d1.refs = refs #self.assertEqual(d1.calls, (4 * 3)) for i in range(3): self.assertEqual(d1.refs[i].value, 0) for i in range(3): for j in range(3): d1.trait_set(exp_object=d1.refs[j], exp_name='value', type_old=None, exp_old=i, type_new=None, exp_new=(i + 1)) d1.refs[j].value = (i + 1) #self.assertEqual(d1.calls, (13 * 3)) for i in range(3): self.assertEqual(d1.refs[i].value, 3) def test_dict2(self): self.check_dict(Dict2(tc=self)) def test_dict3(self): self.check_dict(Dict3(tc=self)) def test_pattern_list1(self): c = Complex(tc=self) self.check_complex(c, c, 'int1, int2, int3', ['int1', 'int2', 'int3'], ['tint1', 'tint2', 'tint3']) def test_pattern_list2(self): c = Complex(tc=self) self.check_complex(c, c, ['int1', 'int2', 'int3'], ['int1', 'int2', 'int3'], ['tint1', 'tint2', 'tint3']) def test_pattern_list3(self): c = Complex(tc=self) self.check_complex(c, c.ref, 'ref.[int1, int2, int3]', ['int1', 'int2', 'int3'], ['tint1', 'tint2', 'tint3']) def test_pattern_list4(self): c = Complex(tc=self) handlers = [c.arg_check0, c.arg_check3, c.arg_check4] n = len(handlers) pattern = 'ref.[int1,int2,int3]' self.multi_register(c, handlers, pattern) r0 = c.ref r1 = ArgCheckBase() c.trait_set(exp_object=c, exp_name='ref', exp_old=r0, exp_new=r1) c.ref = r1 c.trait_set(exp_old=r1, exp_new=r0) c.ref = r0 self.assertEqual(c.calls, 2 * n) self.multi_register(c, handlers, pattern, remove=True) c.ref = r1 c.ref = r0 self.assertEqual(c.calls, 2 * n) def test_pattern_list5(self): c = Complex(tc=self) c.on_trait_change(c.arg_check1, 'ref.[int1,int2,int3]') self.assertRaises(TraitError, c.trait_set, ref=ArgCheckBase()) def test_pattern_list6(self): c = Complex(tc=self) c.on_trait_change(c.arg_check2, 'ref.[int1,int2,int3]') self.assertRaises(TraitError, c.trait_set, ref=ArgCheckBase()) def test_pattern_list7(self): c = Complex(tc=self) self.check_complex(c, c, '+test', ['int1', 'int3', 'tint2'], ['int2', 'tint1', 'tint3']) def test_pattern_list8(self): c = Complex(tc=self) self.check_complex(c, c, 'int+test', ['int1', 'int3'], ['int2', 'tint1', 'tint2', 'tint3']) def test_pattern_list9(self): c = Complex(tc=self) self.check_complex(c, c, 'int-test', ['int2'], ['int1', 'int3', 'tint4', 'tint5', 'tint6']) def test_pattern_list10(self): c = Complex(tc=self) self.check_complex(c, c, 'int+', ['int1', 'int2', 'int3'], ['tint1', 'tint2', 'tint3']) def test_pattern_list11(self): c = Complex(tc=self) self.check_complex(c, c, 'int-', ['int1', 'int2', 'int3'], ['tint1', 'tint2', 'tint3']) def test_pattern_list12(self): c = Complex(tc=self) self.check_complex(c, c, 'int+test,tint-test', ['int1', 'int3', 'tint1', 'tint3'], ['int2', 'tint2']) def test_pattern_list13(self): c = Complex(tc=self) self.check_complex(c, c.ref, 'ref.[int+test,tint-test]', ['int1', 'int3', 'tint1', 'tint3'], ['int2', 'tint2']) def test_cycle1(self): lt = LinkTest(tc=self, head=self.build_list()) handlers = [lt.arg_check0, lt.arg_check1, lt.arg_check2, lt.arg_check3, lt.arg_check4] nh = len(handlers) self.multi_register(lt, handlers, 'head.next*.value') cur = lt.head for i in range(4): lt.trait_set(exp_object=cur, exp_name='value', exp_old=10 * i, exp_new=(10 * i) + 1) cur.value = (10 * i) + 1 cur = cur.next self.assertEqual(lt.calls, 4 * nh) self.multi_register(lt, handlers, 'head.next*.value', remove=True) cur = lt.head for i in range(4): cur.value = (10 * i) + 2 cur = cur.next self.assertEqual(lt.calls, 4 * nh) def test_cycle2(self): lt = LinkTest(tc=self, head=self.build_list()) handlers = [lt.arg_check0, lt.arg_check1, lt.arg_check2, lt.arg_check3, lt.arg_check4] nh = len(handlers) self.multi_register(lt, handlers, 'head.[next,prev]*.value') cur = lt.head for i in range(4): lt.trait_set(exp_object=cur, exp_name='value', exp_old=10 * i, exp_new=(10 * i) + 1) cur.value = (10 * i) + 1 cur = cur.next self.assertEqual(lt.calls, 4 * nh) self.multi_register(lt, handlers, 'head.[next,prev]*.value', remove=True) cur = lt.head for i in range(4): cur.value = (10 * i) + 2 cur = cur.next self.assertEqual(lt.calls, 4 * nh) def test_cycle3(self): lt = LinkTest(tc=self, head=self.build_list()) handlers = [lt.arg_check0, lt.arg_check3, lt.arg_check4] nh = len(handlers) self.multi_register(lt, handlers, 'head.next*.value') link = self.new_link(lt, lt.head, 1) self.assertEqual(lt.calls, nh) link = self.new_link(lt, link, 2) self.assertEqual(lt.calls, 2 * nh) self.multi_register(lt, handlers, 'head.next*.value', remove=True) link = self.new_link(lt, link, 3) self.assertEqual(lt.calls, 2 * nh) def test_property(self): pdo = PropertyDependsOn(tc=self) sum = pdo.sum self.assertEqual(sum, 0) for n in ['int1', 'int2', 'int3']: for i in range(3): pdo.trait_set(exp_old=sum, exp_new=sum + 1) setattr(pdo.ref, n, i + 1) sum += 1 self.assertEqual(pdo.pcalls, (3 * 3) + 1) self.assertEqual(pdo.calls, 3 * 3) for i in range(10): x = pdo.sum self.assertEqual(pdo.pcalls, (3 * 3) + 1) pdo.trait_set(exp_old=sum, exp_new=60) old_ref = pdo.ref pdo.ref = ArgCheckBase(int1=10, int2=20, int3=30) self.assertEqual(pdo.pcalls, (3 * 3) + 2) self.assertEqual(pdo.calls, (3 * 3) + 1) sum = 60 for n in ['int1', 'int2', 'int3']: for i in range(3): pdo.trait_set(exp_old=sum, exp_new=sum + 1) setattr(pdo.ref, n, getattr(pdo.ref, n) + 1) sum += 1 self.assertEqual(pdo.pcalls, (2 * 3 * 3) + 2) self.assertEqual(pdo.calls, (2 * 3 * 3) + 1) for n in ['int1', 'int2', 'int3']: for i in range(3): setattr(old_ref, n, getattr(old_ref, n) + 1) self.assertEqual(pdo.pcalls, (2 * 3 * 3) + 2) self.assertEqual(pdo.calls, (2 * 3 * 3) + 1) self.assertEqual(pdo.sum, sum) self.assertEqual(pdo.pcalls, (2 * 3 * 3) + 2) def check_list(self, l): for i in range(3): ac = ArgCheckBase() self.assertRaises(TraitError, l.refs.append, ac) self.assertEqual(l.calls, 0) for i in range(3): self.assertEqual(l.refs[i].value, 0) refs = [ArgCheckBase(), ArgCheckBase(), ArgCheckBase()] self.assertRaises(TraitError, l.trait_set, refs=refs) self.assertEqual(l.calls, 0) for i in range(3): self.assertEqual(l.refs[i].value, 0) for i in range(3): for j in range(3): l.exp_new = (i + 1) l.refs[j].value = (i + 1) self.assertEqual(l.calls, 0) for i in range(3): self.assertEqual(l.refs[i].value, 3) def check_dict(self, d): for i in range(3): ac = ArgCheckBase() self.assertRaises(TraitError, d.refs.setdefault, i, ac) self.assertEqual(d.calls, 0) for i in range(3): self.assertEqual(d.refs[i].value, 0) refs = {0: ArgCheckBase(), 1: ArgCheckBase(), 2: ArgCheckBase()} self.assertRaises(TraitError, d.trait_set, refs=refs) self.assertEqual(d.calls, 0) for i in range(3): self.assertEqual(d.refs[i].value, 0) for i in range(3): for j in range(3): d.exp_new = (i + 1) d.refs[j].value = (i + 1) self.assertEqual(d.calls, 0) for i in range(3): self.assertEqual(d.refs[i].value, 3) def check_complex(self, c, r, pattern, names, other=[]): handlers = [c.arg_check0, c.arg_check1, c.arg_check2, c.arg_check3, c.arg_check4] nh = len(handlers) nn = len(names) self.multi_register(c, handlers, pattern) for i in range(3): for n in names: c.trait_set(exp_object=r, exp_name=n, exp_old=i, exp_new=(i + 1)) setattr(r, n, i + 1) for n in other: c.trait_set(exp_object=r, exp_name=n, exp_old=i, exp_new=(i + 1)) setattr(r, n, i + 1) self.assertEqual(c.calls, 3 * nn * nh) self.multi_register(c, handlers, pattern, remove=True) for i in range(3): for n in names: setattr(r, n, i + 1) for n in other: setattr(r, n, i + 1) self.assertEqual(c.calls, 3 * nn * nh) def multi_register(self, object, handlers, pattern, remove=False): for handler in handlers: object.on_trait_change(handler, pattern, remove=remove) def build_list(self): l1 = Link(value=00) l2 = Link(value=10) l3 = Link(value=20) l4 = Link(value=30) l1.trait_set(next=l2, prev=l4) l2.trait_set(next=l3, prev=l1) l3.trait_set(next=l4, prev=l2) l4.trait_set(next=l1, prev=l3) return l1 def new_link(self, lt, cur, value): link = Link(value=value, next=cur.next, prev=cur) cur.next.prev = link lt.trait_set(exp_object=cur, exp_name='next', exp_old=cur.next, exp_new=link) cur.next = link return link # Run the unit tests (if invoked from the command line): if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_float.py000066400000000000000000000041421300633736300202220ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # # Copyright (c) 2015, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # ----------------------------------------------------------------------------- """ Tests for the Float trait type. """ import sys from traits.testing.unittest_tools import unittest from ..api import BaseFloat, Float, HasTraits class FloatModel(HasTraits): value = Float class BaseFloatModel(HasTraits): value = BaseFloat class CommonFloatTests(object): """ Common tests for Float and BaseFloat """ def test_default(self): a = self.test_class() self.assertEqual(a.value, 0.0) def test_accepts_float(self): a = self.test_class() a.value = 5.6 self.assertIs(type(a.value), float) self.assertEqual(a.value, 5.6) def test_accepts_int(self): a = self.test_class() a.value = 2 self.assertIs(type(a.value), float) self.assertEqual(a.value, 2.0) @unittest.skipUnless(sys.version_info < (3,), "Not applicable to Python 3") def test_accepts_small_long(self): a = self.test_class() a.value = long(2) self.assertIs(type(a.value), float) self.assertEqual(a.value, 2.0) @unittest.skipUnless(sys.version_info < (3,), "Not applicable to Python 3") def test_accepts_large_long(self): a = self.test_class() # Value large enough to be a long on Python 2. a.value = 2**64 self.assertIs(type(a.value), float) self.assertEqual(a.value, 2**64) class TestFloat(unittest.TestCase, CommonFloatTests): def setUp(self): self.test_class = FloatModel class TestBaseFloat(unittest.TestCase, CommonFloatTests): def setUp(self): self.test_class = BaseFloatModel traits-4.6.0/traits/tests/test_get_traits.py000066400000000000000000000042131300633736300212610ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Int, Str, Undefined, ReadOnly, Float class Foo(HasTraits): num = Int bar = Str class Bar(HasTraits): # Default is visible. PubT1 = Str # Change to invisible. PubT2 = Str( visible=False ) # New behaviour makes private traits invisible. PrivT1 = Str( private=True ) # Force visibility of a private trait. PrivT2 = Str( private=True, visible=True ) class GetTraitTestCase(unittest.TestCase): @unittest.expectedFailure def test_trait_set_bad(self): b = Foo() # This should fail before and after #234. b.num = 'first' self.assertEqual(b.num, 'first') def test_trait_set_replaced(self): b = Foo() # Overriding the trait with a new type should work. b.add_trait( "num", Str() ) b.num = 'first' self.assertEqual(b.num, 'first') def test_trait_set_replaced_and_check(self): b = Foo() b.add_trait( "num", Str() ) b.num = 'first' self.assertEqual(b.num, 'first') # Check that the "traits" call picks up the new instance trait. (See # #234.) self.assertEqual( b.trait("num"), b.traits()["num"] ) def test_trait_names_returned_by_visible_traits(self): b = Bar() self.assertEqual( sorted(b.visible_traits()), sorted(["PubT1", "PrivT2"]) ) ### EOF ####################################################################### if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_int_range_long.py000066400000000000000000000023521300633736300221030ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Int, Range, Long, TraitError class A(HasTraits): i = Int l = Long r = Range(2L, 9223372036854775807L) class TraitIntRangeLong(unittest.TestCase): def test_int(self): "Test to make sure it is legal to set an Int trait to a long value" a = A() a.i = 1 a.i = 10L def test_long(self): "Test if it is legal to set a Long trait to an int value" a = A() a.l = 10 a.l = 100L def test_range(self): "Test a range trait with longs being set to an int value" a = A() a.r = 256 a.r = 20L self.assertRaises(TraitError, a.trait_set, r=1L) self.assertRaises(TraitError, a.trait_set, r=9223372036854775808L) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_integer.py000066400000000000000000000063761300633736300205650ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Tests for the Int trait type. """ from __future__ import absolute_import import decimal import sys try: import numpy except ImportError: numpy_available = False else: numpy_available = True from traits.testing.unittest_tools import unittest from ..api import HasTraits, Int, TraitError class A(HasTraits): integral = Int class IntegerLike(object): def __index__(self): return 42 class Truncatable(object): def __int__(self): return 42 class TestInt(unittest.TestCase): def test_default(self): a = A() self.assertEqual(a.integral, 0) self.assertIs(type(a.integral), int) def test_accepts_int(self): a = A() a.integral = 23 self.assertEqual(a.integral, 23) self.assertIs(type(a.integral), int) def test_accepts_small_long(self): a = A() a.integral = 23L # Check that type is stored as int where possible. self.assertEqual(a.integral, 23) self.assertIs(type(a.integral), int) def test_accepts_large_long(self): a = A() a.integral = long(sys.maxint) self.assertEqual(a.integral, sys.maxint) self.assertIs(type(a.integral), int) a.integral = sys.maxint + 1 self.assertEqual(a.integral, sys.maxint + 1) self.assertIs(type(a.integral), long) def test_accepts_bool(self): a = A() a.integral = True self.assertEqual(a.integral, 1) self.assertIs(type(a.integral), int) def test_respects_dunder_index(self): a = A() a.integral = IntegerLike() self.assertEqual(a.integral, 42) self.assertIs(type(a.integral), int) def test_rejects_dunder_int(self): a = A() with self.assertRaises(TraitError): a.integral = Truncatable() def test_rejects_floating_point_types(self): a = A() with self.assertRaises(TraitError): a.integral = 23.0 with self.assertRaises(TraitError): a.integral = decimal.Decimal(23) def test_rejects_string(self): a = A() with self.assertRaises(TraitError): a.integral = "23" @unittest.skipUnless(numpy_available, "numpy not available") def test_numpy_types(self): a = A() a.integral = numpy.int32(23) self.assertEqual(a.integral, 23) self.assertIn(type(a.integral), (int, long)) a.integral = numpy.uint64(2**63 + 2) self.assertEqual(a.integral, 2**63 + 2) self.assertIs(type(a.integral), long) with self.assertRaises(TraitError): a.integral = numpy.float32(4.0) with self.assertRaises(TraitError): a.integral = numpy.float64(4.0) traits-4.6.0/traits/tests/test_interface_checker.py000066400000000000000000000246001300633736300225420ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt """ Tests to help find out if we can do type-safe casting. """ from __future__ import absolute_import # Standard library imports. from traits.testing.unittest_tools import unittest # Enthought library imports. from traits.adaptation.api import reset_global_adaptation_manager from traits.api import Adapter, HasTraits, Instance, Int, Interface, \ provides, register_factory # Local imports. from traits.interface_checker import InterfaceError, check_implements # Make sure implicit interface checking is turned off, so that we can make the # checks explicitly: from traits import has_traits has_traits.CHECK_INTERFACES = 0 class InterfaceCheckerTestCase(unittest.TestCase): """ Tests to help find out if we can do type-safe casting. """ ########################################################################### # 'TestCase' interface. ########################################################################### def setUp(self): """ Prepares the test fixture before each test method is called. """ reset_global_adaptation_manager() return ########################################################################### # Tests. ########################################################################### def test_non_traits_class(self): """ non-traits class """ class IFoo(Interface): def foo(self): pass # A class that *does* implement the interface. @provides(IFoo) class Foo(object): def foo(self): pass # The checker will raise an exception if the class does not implement # the interface. check_implements(Foo, IFoo, 2) return def test_single_interface(self): """ single interface """ class IFoo(Interface): x = Int # A class that *does* implement the interface. @provides(IFoo) class Foo(HasTraits): x = Int # The checker will raise an exception if the class does not implement # the interface. check_implements(Foo, IFoo, 2) return def test_single_interface_with_invalid_method_signature(self): """ single interface with invalid method signature """ class IFoo(Interface): def foo(self): pass # A class that does *not* implement the interface. @provides(IFoo) class Foo(HasTraits): # Extra argument! def foo(self, x): pass self.assertRaises(InterfaceError, check_implements, Foo, IFoo, 2) return def test_single_interface_with_missing_trait(self): """ single interface with missing trait """ class IFoo(Interface): x = Int # A class that does *not* implement the interface. @provides(IFoo) class Foo(HasTraits): pass self.assertRaises(InterfaceError, check_implements, Foo, IFoo, 2) return def test_single_interface_with_missing_method(self): """ single interface with missing method """ class IFoo(Interface): def method(self): pass # A class that does *not* implement the interface. @provides(IFoo) class Foo(HasTraits): pass self.assertRaises(InterfaceError, check_implements, Foo, IFoo, 2) return def test_multiple_interfaces(self): """ multiple interfaces """ class IFoo(Interface): x = Int class IBar(Interface): y = Int class IBaz(Interface): z = Int # A class that *does* implement the interface. @provides(IFoo, IBar, IBaz) class Foo(HasTraits): x = Int y = Int z = Int # The checker will raise an exception if the class does not implement # the interface. check_implements(Foo, [IFoo, IBar, IBaz], 2) return def test_multiple_interfaces_with_invalid_method_signature(self): """ multiple interfaces with invalid method signature """ class IFoo(Interface): def foo(self): pass class IBar(Interface): def bar(self): pass class IBaz(Interface): def baz(self): pass # A class that does *not* implement the interface. @provides(IFoo, IBar, IBaz) class Foo(HasTraits): def foo(self): pass def bar(self): pass # Extra argument! def baz(self, x): pass self.assertRaises( InterfaceError, check_implements, Foo, [IFoo, IBar, IBaz], 2 ) return def test_multiple_interfaces_with_missing_trait(self): """ multiple interfaces with missing trait """ class IFoo(Interface): x = Int class IBar(Interface): y = Int class IBaz(Interface): z = Int # A class that does *not* implement the interface. @provides(IFoo, IBar, IBaz) class Foo(HasTraits): x = Int y = Int self.assertRaises( InterfaceError, check_implements, Foo, [IFoo, IBar, IBaz], 2 ) return def test_multiple_interfaces_with_missing_method(self): """ multiple interfaces with missing method """ class IFoo(Interface): def foo(self): pass class IBar(Interface): def bar(self): pass class IBaz(Interface): def baz(self): pass # A class that does *not* implement the interface. @provides(IFoo, IBar, IBaz) class Foo(HasTraits): def foo(self): pass def bar(self): pass self.assertRaises( InterfaceError, check_implements, Foo, [IFoo, IBar, IBaz], 2 ) return def test_inherited_interfaces(self): """ inherited interfaces """ class IFoo(Interface): x = Int class IBar(IFoo): y = Int class IBaz(IBar): z = Int # A class that *does* implement the interface. @provides(IBaz) class Foo(HasTraits): x = Int y = Int z = Int # The checker will raise an exception if the class does not implement # the interface. check_implements(Foo, IBaz, 2) return def test_inherited_interfaces_with_invalid_method_signature(self): """ inherited with invalid method signature """ class IFoo(Interface): def foo(self): pass class IBar(IFoo): def bar(self): pass class IBaz(IBar): def baz(self): pass # A class that does *not* implement the interface. @provides(IBaz) class Foo(HasTraits): def foo(self): pass def bar(self): pass # Extra argument! def baz(self, x): pass self.assertRaises(InterfaceError, check_implements, Foo, IBaz, 2) return def test_inherited_interfaces_with_missing_trait(self): """ inherited interfaces with missing trait """ class IFoo(Interface): x = Int class IBar(IFoo): y = Int class IBaz(IBar): z = Int # A class that does *not* implement the interface. @provides(IBaz) class Foo(HasTraits): x = Int y = Int self.assertRaises(InterfaceError, check_implements, Foo, IBaz, 2) return def test_inherited_interfaces_with_missing_method(self): """ inherited interfaces with missing method """ class IFoo(Interface): def foo(self): pass class IBar(IFoo): def bar(self): pass class IBaz(IBar): def baz(self): pass # A class that does *not* implement the interface. @provides(IBaz) class Foo(HasTraits): def foo(self): pass def bar(self): pass self.assertRaises(InterfaceError, check_implements, Foo, IBaz, 2) return def test_subclasses_with_wrong_signature_methods(self): """ Subclasses with incorrect method signatures """ class IFoo(Interface): def foo(self, argument): pass @provides(IFoo) class Foo(HasTraits): def foo(self, argument): pass class Bar(Foo): def foo(self): pass self.assertRaises(InterfaceError, check_implements, Bar, IFoo, 2) # Make sure interfaces and adaptation etc still work with the 'HasTraits' # version of 'Interface'! def test_instance(self): """ instance """ class IFoo(Interface): pass @provides(IFoo) class Foo(HasTraits): pass class Bar(HasTraits): foo = Instance(IFoo) b = Bar(foo=Foo()) return def test_callable(self): """ callable """ class IFoo(Interface): pass @provides(IFoo) class Foo(HasTraits): pass f = Foo() self.assertEqual(f, IFoo(f)) return def test_adaptation(self): """ adaptation """ class IFoo(Interface): pass class Foo(HasTraits): pass @provides(IFoo) class FooToIFooAdapter(Adapter): pass register_factory(FooToIFooAdapter, Foo, IFoo) f = Foo() # Make sure adaptation works. i_foo = IFoo(f) self.assertNotEqual(None, i_foo) self.assertEqual(FooToIFooAdapter, type(i_foo)) return # Entry point for stand-alone testing. if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_interfaces.py000066400000000000000000000215601300633736300212430ustar00rootroot00000000000000# Unit test case for testing interfaces and adaptation. # # Written by: David C. Morrill # # Date: 4/10/2007 # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt """ Unit test case for testing interfaces and adaptation. """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from traits.api import (HasTraits, Adapter, AdaptsTo, Instance, Int, Interface, List, provides, register_factory, Supports, TraitError) from traits.adaptation.api import reset_global_adaptation_manager class IFoo(Interface): def get_foo(self): """ Returns the current foo. """ class IFooPlus(IFoo): def get_foo_plus(self): """ Returns even more foo. """ class IAverage(Interface): def get_average(self): """ Returns the average value for the object. """ class IList(Interface): def get_list(self): """ Returns the list value for the object. """ class Sample(HasTraits): s1 = Int(1, sample=True) s2 = Int(2, sample=True) s3 = Int(3, sample=True) i1 = Int(4) i2 = Int(5) i3 = Int(6) @provides(IList) class SampleList(HasTraits): """SampleList docstring.""" data = List(Int, [10, 20, 30]) def get_list(self): return self.data @provides(IList, IAverage) class SampleAverage(HasTraits): data = List(Int, [100, 200, 300]) def get_list(self): return self.data def get_average(self): value = self.get_list() if len(value) == 0: return 0.0 average = 0.0 for item in value: average += item return (average / len(value)) class SampleBad(HasTraits): pass class TraitsHolder(HasTraits): a_no = Instance(IAverage, adapt='no') a_yes = Instance(IAverage, adapt='yes') a_default = Instance(IAverage, adapt='default') list_adapted_to = Supports(IList) foo_adapted_to = Supports(IFoo) foo_plus_adapted_to = Supports(IFooPlus) list_adapts_to = AdaptsTo(IList) foo_adapts_to = AdaptsTo(IFoo) foo_plus_adapts_to = AdaptsTo(IFooPlus) class SampleListAdapter(Adapter): def get_list(self): obj = self.adaptee return [getattr(obj, name) for name in obj.trait_names(sample=True)] class ListAverageAdapter(Adapter): def get_average(self): value = self.adaptee.get_list() if len(value) == 0: return 0.0 average = 0.0 for item in value: average += item return (average / len(value)) class SampleFooAdapter(HasTraits): object = Instance(Sample) def __init__(self, object): self.object = object def get_foo(self): object = self.object return (object.s1 + object.s2 + object.s3) class FooPlusAdapter(object): def __init__(self, obj): self.obj = obj def get_foo(self): return self.obj.get_foo() def get_foo_plus(self): return (self.obj.get_foo() + 1) class InterfacesTest(unittest.TestCase): #### 'TestCase' protocol ################################################## def setUp(self): reset_global_adaptation_manager() # Register adapters. register_factory(SampleListAdapter, Sample, IList) register_factory(ListAverageAdapter, IList, IAverage) register_factory(SampleFooAdapter, Sample, IFoo) register_factory(FooPlusAdapter, IFoo, IFooPlus) #### Tests ################################################################ def test_provides_none(self): @provides() class Test(HasTraits): pass def test_provides_one(self): @provides(IFoo) class Test(HasTraits): pass def test_provides_multi(self): @provides(IFoo, IAverage, IList) class Test (HasTraits): pass def test_provides_extended(self): """ Ensure that subclasses of Interfaces imply the superinterface. """ @provides(IFooPlus) class Test(HasTraits): pass ta = TraitsHolder() ta.foo_adapted_to = Test() def test_provides_bad(self): with self.assertRaises(Exception): @provides(Sample) class Test(HasTraits): pass def test_instance_adapt_no(self): ta = TraitsHolder() # Verify that SampleAverage() does not raise an error (it is an # instance of the IAverage interface). try: ta.a_no = SampleAverage() except TraitError: self.fail("Setting instance of interface should not require " "adaptation") # These are not instances of the IAverage interface, and therefore # cannot be set to the trait. self.assertRaises(TraitError, ta.trait_set, a_no=SampleList()) self.assertRaises(TraitError, ta.trait_set, a_no=Sample()) self.assertRaises(TraitError, ta.trait_set, a_no=SampleBad()) def test_instance_adapt_yes(self): ta = TraitsHolder() ta.a_yes = object = SampleAverage() self.assertEqual(ta.a_yes.get_average(), 200.0) self.assertIsInstance(ta.a_yes, SampleAverage) self.assertFalse(hasattr(ta, 'a_yes_')) ta.a_yes = object = SampleList() self.assertEqual(ta.a_yes.get_average(), 20.0) self.assertIsInstance(ta.a_yes, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_yes_')) ta.a_yes = object = Sample() self.assertEqual(ta.a_yes.get_average(), 2.0) self.assertIsInstance(ta.a_yes, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_yes_')) self.assertRaises(TraitError, ta.trait_set, a_yes=SampleBad()) def test_instance_adapt_default(self): ta = TraitsHolder() ta.a_default = object = SampleAverage() self.assertEqual(ta.a_default.get_average(), 200.0) self.assertIsInstance(ta.a_default, SampleAverage) self.assertFalse(hasattr(ta, 'a_default_')) ta.a_default = object = SampleList() self.assertEqual(ta.a_default.get_average(), 20.0) self.assertIsInstance(ta.a_default, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_default_')) ta.a_default = object = Sample() self.assertEqual(ta.a_default.get_average(), 2.0) self.assertIsInstance(ta.a_default, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_default_')) ta.a_default = object = SampleBad() self.assertEqual(ta.a_default, None) self.assertFalse(hasattr(ta, 'a_default_')) def test_adapted_to(self): ta = TraitsHolder() ta.list_adapted_to = object = Sample() result = ta.list_adapted_to.get_list() self.assertEqual(len(result), 3) for n in [1, 2, 3]: self.assertIn(n, result) self.assertIsInstance(ta.list_adapted_to, SampleListAdapter) self.assertEqual(ta.list_adapted_to_, object) ta.foo_adapted_to = object = Sample() self.assertEqual(ta.foo_adapted_to.get_foo(), 6) self.assertIsInstance(ta.foo_adapted_to, SampleFooAdapter) self.assertEqual(ta.foo_adapted_to_, object) ta.foo_plus_adapted_to = object = Sample(s1=5, s2=10, s3=15) self.assertEqual(ta.foo_plus_adapted_to.get_foo(), 30) self.assertEqual(ta.foo_plus_adapted_to.get_foo_plus(), 31) self.assertIsInstance(ta.foo_plus_adapted_to, FooPlusAdapter) self.assertEqual(ta.foo_plus_adapted_to_, object) def test_adapts_to(self): ta = TraitsHolder() ta.list_adapts_to = object = Sample() self.assertEqual(ta.list_adapts_to, object) result = ta.list_adapts_to_.get_list() self.assertEqual(len(result), 3) for n in [1, 2, 3]: self.assertIn(n, result) self.assertIsInstance(ta.list_adapts_to_, SampleListAdapter) ta.foo_adapts_to = object = Sample() self.assertEqual(ta.foo_adapts_to, object) self.assertEqual(ta.foo_adapts_to_.get_foo(), 6) self.assertIsInstance(ta.foo_adapts_to_, SampleFooAdapter) ta.foo_plus_adapts_to = object = Sample(s1=5, s2=10, s3=15) self.assertEqual(ta.foo_plus_adapts_to, object) self.assertEqual(ta.foo_plus_adapts_to_.get_foo(), 30) self.assertEqual(ta.foo_plus_adapts_to_.get_foo_plus(), 31) self.assertIsInstance(ta.foo_plus_adapts_to_, FooPlusAdapter) def test_decorated_class_name_and_docstring(self): self.assertEqual(SampleList.__name__, 'SampleList') self.assertEqual(SampleList.__doc__, "SampleList docstring.") # Run the unit tests (if invoked from the command line): if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_interfaces_with_implements.py000066400000000000000000000235071300633736300245360ustar00rootroot00000000000000# Unit test case for testing interfaces and adaptation. # # Written by: David C. Morrill # # Date: 4/10/2007 # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt """ Unit test case for testing interfaces and adaptation. This file is equivalent to test_interfaces.py, only using the deprecated 'implements' and 'adapts' functions. """ from __future__ import absolute_import import sys from traits.testing.unittest_tools import unittest from traits.adaptation.api import get_global_adaptation_manager, \ set_global_adaptation_manager from traits.api import HasTraits, Adapter, adapts, AdaptsTo, \ implements, Instance, Int, Interface, List, Supports, TraitError # Using the deprecated class advisor "adapts", the registration of adapters # occurs globally at class definition time. Since other tests will reset the # global adaptation manager, the registration will be lost. # That's why we save a reference to the current global adaptation manager. _adaptation_manager = get_global_adaptation_manager() #------------------------------------------------------------------------------ # Test 'Interface' definitions: #------------------------------------------------------------------------------ # 'adapts' and 'implements' are not supported in Python 3. if sys.version_info < (3,): class IFoo(Interface): def get_foo(self): """ Returns the current foo. """ class IFooPlus(IFoo): def get_foo_plus(self): """ Returns even more foo. """ class IAverage(Interface): def get_average(self): """ Returns the average value for the object. """ class IList(Interface): def get_list(self): """ Returns the list value for the object. """ class Sample(HasTraits): s1 = Int(1, sample=True) s2 = Int(2, sample=True) s3 = Int(3, sample=True) i1 = Int(4) i2 = Int(5) i3 = Int(6) class SampleList(HasTraits): implements(IList) data = List(Int, [10, 20, 30]) def get_list(self): return self.data class SampleAverage(HasTraits): implements(IList, IAverage) data = List(Int, [100, 200, 300]) def get_list(self): return self.data def get_average(self): value = self.get_list() if len(value) == 0: return 0.0 average = 0.0 for item in value: average += item return (average / len(value)) class SampleBad(HasTraits): pass class TraitsHolder(HasTraits): a_no = Instance(IAverage, adapt='no') a_yes = Instance(IAverage, adapt='yes') a_default = Instance(IAverage, adapt='default') list_adapted_to = Supports(IList) foo_adapted_to = Supports(IFoo) foo_plus_adapted_to = Supports(IFooPlus) list_adapts_to = AdaptsTo(IList) foo_adapts_to = AdaptsTo(IFoo) foo_plus_adapts_to = AdaptsTo(IFooPlus) class SampleListAdapter(Adapter): adapts(Sample, IList) def get_list(self): obj = self.adaptee return [getattr(obj, name) for name in obj.trait_names(sample=True)] class ListAverageAdapter(Adapter): adapts(IList, IAverage) def get_average(self): value = self.adaptee.get_list() if len(value) == 0: return 0.0 average = 0.0 for item in value: average += item return (average / len(value)) class SampleFooAdapter(HasTraits): adapts(Sample, IFoo) object = Instance(Sample) def __init__(self, object): self.object = object def get_foo(self): object = self.object return (object.s1 + object.s2 + object.s3) class FooPlusAdapter(object): def __init__(self, obj): self.obj = obj def get_foo(self): return self.obj.get_foo() def get_foo_plus(self): return (self.obj.get_foo() + 1) adapts(FooPlusAdapter, IFoo, IFooPlus) @unittest.skipUnless(sys.version_info < (3,), "The 'adapts' and 'implements' class advisors " "are not supported in Python 3.") class InterfacesTest(unittest.TestCase): #### 'TestCase' protocol ################################################## def setUp(self): set_global_adaptation_manager(_adaptation_manager) #### Tests ################################################################ def test_implements_none(self): class Test(HasTraits): implements() def test_implements_one(self): class Test(HasTraits): implements(IFoo) def test_implements_multi(self): class Test(HasTraits): implements(IFoo, IAverage, IList) def test_implements_extended(self): """ Ensure that subclasses of Interfaces imply the superinterface. """ class Test(HasTraits): implements(IFooPlus) ta = TraitsHolder() ta.foo_adapted_to = Test() def test_implements_bad(self): self.assertRaises(TraitError, self.implements_bad) def test_instance_adapt_no(self): ta = TraitsHolder() # Verify that SampleAverage() does not raise an error (it is an # instance of the IAverage interface). try: ta.a_no = SampleAverage() except TraitError: self.fail("Setting instance of interface should not require " "adaptation") # These are not instances of the IAverage interface, and therefore # cannot be set to the trait. self.assertRaises(TraitError, ta.trait_set, a_no=SampleList()) self.assertRaises(TraitError, ta.trait_set, a_no=Sample()) self.assertRaises(TraitError, ta.trait_set, a_no=SampleBad()) def test_instance_adapt_yes(self): ta = TraitsHolder() ta.a_yes = object = SampleAverage() self.assertEqual(ta.a_yes.get_average(), 200.0) self.assertIsInstance(ta.a_yes, SampleAverage) self.assertFalse(hasattr(ta, 'a_yes_')) ta.a_yes = object = SampleList() self.assertEqual(ta.a_yes.get_average(), 20.0) self.assertIsInstance(ta.a_yes, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_yes_')) ta.a_yes = object = Sample() self.assertEqual(ta.a_yes.get_average(), 2.0) self.assertIsInstance(ta.a_yes, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_yes_')) self.assertRaises(TraitError, ta.trait_set, a_yes=SampleBad()) def test_instance_adapt_default(self): ta = TraitsHolder() ta.a_default = object = SampleAverage() self.assertEqual(ta.a_default.get_average(), 200.0) self.assertIsInstance(ta.a_default, SampleAverage) self.assertFalse(hasattr(ta, 'a_default_')) ta.a_default = object = SampleList() self.assertEqual(ta.a_default.get_average(), 20.0) self.assertIsInstance(ta.a_default, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_default_')) ta.a_default = object = Sample() self.assertEqual(ta.a_default.get_average(), 2.0) self.assertIsInstance(ta.a_default, ListAverageAdapter) self.assertFalse(hasattr(ta, 'a_default_')) ta.a_default = object = SampleBad() self.assertEqual(ta.a_default, None) self.assertFalse(hasattr(ta, 'a_default_')) def test_adapted_to(self): ta = TraitsHolder() ta.list_adapted_to = object = Sample() result = ta.list_adapted_to.get_list() self.assertEqual(len(result), 3) for n in [1, 2, 3]: self.assertIn(n, result) self.assertIsInstance(ta.list_adapted_to, SampleListAdapter) self.assertEqual(ta.list_adapted_to_, object) ta.foo_adapted_to = object = Sample() self.assertEqual(ta.foo_adapted_to.get_foo(), 6) self.assertIsInstance(ta.foo_adapted_to, SampleFooAdapter) self.assertEqual(ta.foo_adapted_to_, object) ta.foo_plus_adapted_to = object = Sample(s1=5, s2=10, s3=15) self.assertEqual(ta.foo_plus_adapted_to.get_foo(), 30) self.assertEqual(ta.foo_plus_adapted_to.get_foo_plus(), 31) self.assertIsInstance(ta.foo_plus_adapted_to, FooPlusAdapter) self.assertEqual(ta.foo_plus_adapted_to_, object) def test_adapts_to(self): ta = TraitsHolder() ta.list_adapts_to = object = Sample() self.assertEqual(ta.list_adapts_to, object) result = ta.list_adapts_to_.get_list() self.assertEqual(len(result), 3) for n in [1, 2, 3]: self.assertIn(n, result) self.assertIsInstance(ta.list_adapts_to_, SampleListAdapter) ta.foo_adapts_to = object = Sample() self.assertEqual(ta.foo_adapts_to, object) self.assertEqual(ta.foo_adapts_to_.get_foo(), 6) self.assertIsInstance(ta.foo_adapts_to_, SampleFooAdapter) ta.foo_plus_adapts_to = object = Sample(s1=5, s2=10, s3=15) self.assertEqual(ta.foo_plus_adapts_to, object) self.assertEqual(ta.foo_plus_adapts_to_.get_foo(), 30) self.assertEqual(ta.foo_plus_adapts_to_.get_foo_plus(), 31) self.assertIsInstance(ta.foo_plus_adapts_to_, FooPlusAdapter) #-- Helper Methods -------------------------------------------------------- def implements_bad(self): class Test(HasTraits): implements(Sample) # Run the unit tests (if invoked from the command line): if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_keyword_args.py000066400000000000000000000015021300633736300216120ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from ..api import HasTraits, Instance, Int from traits.testing.unittest_tools import unittest class Bar(HasTraits): b = Int(3) class Foo(HasTraits): bar = Instance(Bar) class KeyWordArgsTest(unittest.TestCase): def test_using_kw(self): bar = Bar(b=5) foo = Foo(bar=bar) self.assertEqual(foo.bar.b, 5) def test_not_using_kw(self): foo = Foo() self.assertEqual(foo.bar, None) traits-4.6.0/traits/tests/test_list.py000066400000000000000000000250341300633736300200730ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import import sys from traits.testing.unittest_tools import unittest from ..api import CList, HasTraits, Instance, Int, List, Str, TraitError class Foo(HasTraits): l = List(Str) class Bar(HasTraits): name = Str class Baz(HasTraits): bars = List(Bar) class BazRef(HasTraits): bars = List(Bar, copy='ref') class DeepBaz(HasTraits): baz = Instance(Baz) class DeepBazBazRef(HasTraits): baz = Instance(BazRef) class CFoo(HasTraits): ints = CList(Int) strs = CList(Str) class ListTestCase(unittest.TestCase): def test_initialized(self): f = Foo() self.assertNotEqual(f.l, None) self.assertEqual(len(f.l), 0) return def test_initializer(self): f = Foo(l=['a', 'list']) self.assertNotEqual(f.l, None) self.assertEqual(f.l, ['a', 'list']) return def test_type_check(self): f = Foo() f.l.append('string') self.assertRaises(TraitError, f.l.append, 123.456) return def test_append(self): f = Foo() f.l.append('bar') self.assertEqual(f.l, ['bar']) return def test_remove(self): f = Foo() f.l.append('bar') f.l.remove('bar') self.assertEqual(f.l, []) return def test_slice(self): f = Foo(l=['zero', 'one', 'two', 'three']) self.assertEqual(f.l[0], 'zero') self.assertEqual(f.l[:0], []) self.assertEqual(f.l[:1], ['zero']) self.assertEqual(f.l[0:1], ['zero']) self.assertEqual(f.l[1:], ['one', 'two', 'three']) self.assertEqual(f.l[-1], 'three') self.assertEqual(f.l[-2], 'two') self.assertEqual(f.l[:-1], ['zero', 'one', 'two']) return def test_slice_assignment(self): # Exhaustive testing. starts = stops = [None] + range(-10, 11) steps = list(starts) steps.remove(0) test_slices = [slice(start, stop, step) for start in starts for stop in stops for step in steps] for test_slice in test_slices: f = Foo(l=['zero', 'one', 'two', 'three', 'four']) plain_l = list(f.l) length = len(plain_l[test_slice]) replacements = map(str, range(length)) # Plain Python list and Traits list behaviour should match. plain_l[test_slice] = replacements f.l[test_slice] = replacements self.assertEqual( f.l, plain_l, "failed for slice {0!r}".format(test_slice)) def test_slice_assignments_of_different_length(self): # Test slice assignments where rhs has a different length # to the slice. These should work only for slices of step 1. test_list = ['zero', 'one', 'two', 'three'] f = Foo(l=test_list) f.l[1:3] = '01234' self.assertEqual(f.l, ['zero', '0', '1', '2', '3', '4', 'three']) f.l[4:] = [] self.assertEqual(f.l, ['zero', '0', '1', '2']) f.l[:] = 'abcde' self.assertEqual(f.l, ['a', 'b', 'c', 'd', 'e']) f.l[:] = [] self.assertEqual(f.l, []) f = Foo(l=test_list) with self.assertRaises(ValueError): f.l[::2] = ['a', 'b', 'c'] self.assertEqual(f.l, test_list) with self.assertRaises(ValueError): f.l[::-1] = [] self.assertEqual(f.l, test_list) def test_slice_deletion_bad_length_computation(self): # Regression test for enthought/traits#283. class IHasConstrainedList(HasTraits): foo = List(Str, minlen=3) f = IHasConstrainedList(foo=['zero', 'one', 'two', 'three']) # We're deleting two items; this should raise. with self.assertRaises(TraitError): del f.foo[::3] def test_retrieve_reference(self): f = Foo(l=['initial', 'value']) l = f.l self.assertIs(l, f.l) # no copy on change behavior, l is always a reference l.append('change') self.assertEqual(f.l, ['initial', 'value', 'change']) f.l.append('more change') self.assertEqual(l, ['initial', 'value', 'change', 'more change']) return def test_assignment_makes_copy(self): f = Foo(l=['initial', 'value']) l = ['new'] f.l = l # same content self.assertEqual(l, f.l) # different objects self.assertIsNot(l, f.l) # which means behaviorally... l.append('l change') self.assertNotIn('l change', f.l) f.l.append('f.l change') self.assertNotIn('f.l change', l) return def test_should_not_allow_none(self): f = Foo(l=['initial', 'value']) try: f.l = None self.fail('None assigned to List trait.') except TraitError: pass def test_clone(self): baz = Baz() for name in ['a', 'b', 'c', 'd']: baz.bars.append(Bar(name=name)) # Clone will clone baz, the bars list, and the objects in the list baz_copy = baz.clone_traits() self.assertIsNot(baz_copy, baz) self.assertIsNot(baz_copy.bars, baz.bars) self.assertEqual(len(baz_copy.bars), len(baz.bars)) for bar in baz.bars: self.assertNotIn(bar, baz_copy.bars) baz_bar_names = [bar.name for bar in baz.bars] baz_copy_bar_names = [bar.name for bar in baz_copy.bars] baz_bar_names.sort() baz_copy_bar_names.sort() self.assertEqual(baz_copy_bar_names, baz_bar_names) return def test_clone_ref(self): baz = BazRef() for name in ['a', 'b', 'c', 'd']: baz.bars.append(Bar(name=name)) # Clone will clone baz, the bars list, but the objects in the list # will not be cloned because the copy metatrait of the List is 'ref' baz_copy = baz.clone_traits() self.assertIsNot(baz_copy, baz) self.assertIsNot(baz_copy.bars, baz.bars) self.assertEqual(len(baz_copy.bars), len(baz.bars)) for bar in baz.bars: self.assertIn(bar, baz_copy.bars) return def test_clone_deep_baz(self): baz = Baz() for name in ['a', 'b', 'c', 'd']: baz.bars.append(Bar(name=name)) deep_baz = DeepBaz(baz=baz) # Clone will clone deep_baz, deep_baz.baz, the bars list, # and the objects in the list deep_baz_copy = deep_baz.clone_traits() self.assertIsNot(deep_baz_copy, deep_baz) self.assertIsNot(deep_baz_copy.baz, deep_baz.baz) baz_copy = deep_baz_copy.baz self.assertIsNot(baz_copy, baz) self.assertIsNot(baz_copy.bars, baz.bars) self.assertEqual(len(baz_copy.bars), len(baz.bars)) for bar in baz.bars: self.assertNotIn(bar, baz_copy.bars) baz_bar_names = [bar.name for bar in baz.bars] baz_copy_bar_names = [bar.name for bar in baz_copy.bars] baz_bar_names.sort() baz_copy_bar_names.sort() self.assertEqual(baz_copy_bar_names, baz_bar_names) return def test_clone_deep_baz_ref(self): baz = BazRef() for name in ['a', 'b', 'c', 'd']: baz.bars.append(Bar(name=name)) deep_baz = DeepBazBazRef(baz=baz) deep_baz_copy = deep_baz.clone_traits() self.assertIsNot(deep_baz_copy, deep_baz) self.assertIsNot(deep_baz_copy.baz, deep_baz.baz) baz_copy = deep_baz_copy.baz self.assertIsNot(baz_copy, baz) self.assertIsNot(baz_copy.bars, baz.bars) self.assertEqual(len(baz_copy.bars), len(baz.bars)) for bar in baz.bars: self.assertIn(bar, baz_copy.bars) return def test_coercion(self): f = CFoo() # Test coercion from basic built-in types f.ints = [1, 2, 3] desired = [1, 2, 3] self.assertEqual(f.ints, desired) f.ints = (1, 2, 3) self.assertEqual(f.ints, desired) f.strs = ("abc", "def", "ghi") self.assertEqual(f.strs, ["abc", "def", "ghi"]) f.strs = "abcdef" self.assertEqual(f.strs, list("abcdef")) try: from numpy import array except ImportError: pass else: if sys.version_info[0] < 3: f.ints = array([1, 2, 3]) self.assertEqual(f.ints, [1, 2, 3]) else: # These would fail due to np.int_ being an invalid vallue # for the Int-trait. pass f.strs = array(("abc", "def", "ghi")) self.assertEqual(f.strs, ["abc", "def", "ghi"]) def test_extend(self): f = Foo() f.l = ['4', '5', '6'] f.l.extend(['1', '2', '3']) self.assertEqual(f.l, ['4', '5', '6', '1', '2', '3']) def test_iadd(self): f = Foo() f.l = ['4', '5', '6'] f.l += ['1', '2', '3'] self.assertEqual(f.l, ['4', '5', '6', '1', '2', '3']) def test_imul(self): f = Foo() f.l = list('123') f.l *= 4 self.assertEqual(f.l, list('123123123123')) def test_sort_no_args(self): f = Foo() f.l = ["a", "c", "b", "d"] f.l.sort() self.assertEqual(f.l, ["a", "b", "c", "d"]) def test_sort_key(self): f = Foo() f.l = ["a", "c", "b", "d"] f.l.sort(key=lambda x: -ord(x)) self.assertEqual(f.l, ["d", "c", "b", "a"]) def test_sort_reverse(self): f = Foo() f.l = ["a", "c", "b", "d"] f.l.sort(reverse=True) self.assertEqual(f.l, ["d", "c", "b", "a"]) def test_sort_key_reverse(self): f = Foo() f.l = ["a", "c", "b", "d"] f.l.sort(key=lambda x: -ord(x), reverse=True) self.assertEqual(f.l, ["a", "b", "c", "d"]) @unittest.skipIf(sys.version_info[0] >= 3, "Not for Python 3") def test_sort_cmp(self): f = Foo() f.l = ["a", "c", "b", "d"] f.l.sort(cmp=lambda x, y: ord(x) - ord(y)) self.assertEqual(f.l, ["a", "b", "c", "d"]) @unittest.skipIf(sys.version_info[0] < 3, "Not for Python 2") def test_sort_cmp_error(self): f = Foo() f.l = ["a", "c", "b", "d"] with self.assertRaises(TypeError): f.l.sort(cmp=lambda x, y: ord(x) - ord(y)) traits-4.6.0/traits/tests/test_list_events.py000066400000000000000000000137701300633736300214630ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Tests for List items_changed events. """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Int, List class MyClass(HasTraits): l = List(Int, [1, 2, 3]) l_events = List def _l_items_changed(self, event): self.l_events.append(event) class ListEventTestCase(unittest.TestCase): def test_initialization(self): # Just creating an instance of MyClass shouldn't cause # the items_changed handler to fire. foo = MyClass() self.assertEqual(foo.l, [1, 2, 3]) self.assertEqual(len(foo.l_events), 0) def test_append(self): foo = MyClass() foo.l.append(4) self.assertEqual(foo.l, [1, 2, 3, 4]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [4]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 3) def test_extend(self): foo = MyClass() foo.l.extend([4, 5, 6]) self.assertEqual(foo.l, [1, 2, 3, 4, 5, 6]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [4, 5, 6]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 3) def test_extend_via_inplace_addition(self): foo = MyClass() foo.l += [4, 5, 6] self.assertEqual(foo.l, [1, 2, 3, 4, 5, 6]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [4, 5, 6]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 3) def test_insert(self): foo = MyClass() foo.l.insert(1, 99) self.assertEqual(foo.l, [1, 99, 2, 3]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [99]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 1) def test_insert_with_negative_argument(self): foo = MyClass() foo.l.insert(-1, 99) self.assertEqual(foo.l, [1, 2, 99, 3]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [99]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 2) def test_insert_index_invariants(self): # Note that Python's list.insert allows indices outside # the range [-len(my_list), len(my_list)]. for index in range(-10, 10): foo = MyClass() foo.l.insert(index, 1729) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [1729]) self.assertEqual(event.removed, []) self.assertGreaterEqual(event.index, 0) self.assertEqual(foo.l[event.index], 1729) def test_pop_with_no_argument(self): foo = MyClass() item = foo.l.pop() self.assertEqual(item, 3) self.assertEqual(foo.l, [1, 2]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, []) self.assertEqual(event.removed, [3]) self.assertEqual(event.index, 2) def test_pop(self): foo = MyClass() item = foo.l.pop(0) self.assertEqual(item, 1) self.assertEqual(foo.l, [2, 3]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, []) self.assertEqual(event.removed, [1]) self.assertEqual(event.index, 0) def test_pop_with_negative_argument(self): foo = MyClass() item = foo.l.pop(-2) self.assertEqual(item, 2) self.assertEqual(foo.l, [1, 3]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, []) self.assertEqual(event.removed, [2]) self.assertEqual(event.index, 1) def test_pop_out_of_range(self): foo = MyClass() with self.assertRaises(IndexError): foo.l.pop(-4) with self.assertRaises(IndexError): foo.l.pop(3) self.assertEqual(foo.l, [1, 2, 3]) self.assertEqual(len(foo.l_events), 0) def test_remove(self): foo = MyClass() foo.l.remove(2) self.assertEqual(foo.l, [1, 3]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, []) self.assertEqual(event.removed, [2]) self.assertEqual(event.index, 1) def test_remove_item_not_present(self): foo = MyClass() with self.assertRaises(ValueError): foo.l.remove(1729) self.assertEqual(foo.l, [1, 2, 3]) self.assertEqual(len(foo.l_events), 0) def test_inplace_multiply(self): foo = MyClass() foo.l *= 2 self.assertEqual(foo.l, [1, 2, 3, 1, 2, 3]) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, [1, 2, 3]) self.assertEqual(event.removed, []) self.assertEqual(event.index, 3) def test_inplace_multiply_by_zero(self): foo = MyClass() foo.l *= 0 self.assertEqual(foo.l, []) self.assertEqual(len(foo.l_events), 1) event = foo.l_events[0] self.assertEqual(event.added, []) self.assertEqual(event.removed, [1, 2, 3]) self.assertEqual(event.index, 0) traits-4.6.0/traits/tests/test_listeners.py000066400000000000000000000161351300633736300211320ustar00rootroot00000000000000# Test the 'add_trait_listener', 'remove_trait_listener' interface to # the HasTraits class. # # Written by: David C. Morrill # # Date: 09/07/2005 # # (c) Copyright 2005 by Enthought, Inc. # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # License included in /LICENSE.txt and may be redistributed only under the # conditions described in the aforementioned license. The license is also # available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! from __future__ import absolute_import import contextlib import cStringIO import sys import threading import time from traits.testing.unittest_tools import unittest from ..api import HasTraits, Str, Int, Float, Any, Event from ..api import push_exception_handler, pop_exception_handler @contextlib.contextmanager def captured_stderr(): """ Return a context manager that directs all stderr output to a string. """ new_stderr = cStringIO.StringIO() original_stderr = sys.stderr sys.stderr = new_stderr try: yield new_stderr finally: sys.stderr = original_stderr class GenerateEvents(HasTraits): name = Str age = Int weight = Float events = {} # dict of events class ListenEvents(HasTraits): # 'GenerateEvents' event interface: # the events are stored in the dict 'events' def _name_changed(self, object, name, old, new): events["_name_changed"] = (name, old, new) def _age_changed(self, object, name, old, new): events["_age_changed"] = (name, old, new) def _weight_changed(self, object, name, old, new): events["_weight_changed"] = (name, old, new) def alt_name_changed(self, object, name, old, new): events["alt_name_changed"] = (name, old, new) def alt_weight_changed(self, object, name, old, new): events["alt_weight_changed"] = (name, old, new) class GenerateFailingEvents(HasTraits): name = Str def _name_changed(self): raise RuntimeError class Test_Listeners(unittest.TestCase): def test(self): global events # FIXME: comparing floats ge = GenerateEvents() le = ListenEvents() # Starting test: No Listeners ge.trait_set(name='Joe', age=22, weight=152.0) # Adding default listener ge.add_trait_listener(le) events = {} ge.trait_set(name='Mike', age=34, weight=178.0) self.assertEqual(events, { '_age_changed': ('age', 22, 34), '_weight_changed': ('weight', 152.0, 178.0), '_name_changed': ('name', 'Joe', 'Mike'), }) # Adding alternate listener ge.add_trait_listener(le, 'alt') events = {} ge.trait_set(name='Gertrude', age=39, weight=108.0) self.assertEqual(events, { '_age_changed': ('age', 34, 39), '_name_changed': ('name', 'Mike', 'Gertrude'), '_weight_changed': ('weight', 178.0, 108.0), 'alt_name_changed': ('name', 'Mike', 'Gertrude'), 'alt_weight_changed': ('weight', 178.0, 108.0), }) # Removing default listener ge.remove_trait_listener(le) events = {} ge.trait_set(name='Sally', age=46, weight=118.0) self.assertEqual(events, { 'alt_name_changed': ('name', 'Gertrude', 'Sally'), 'alt_weight_changed': ('weight', 108.0, 118.0), }) # Removing alternate listener ge.remove_trait_listener(le, 'alt') events = {} ge.trait_set(name='Ralph', age=29, weight=198.0) self.assertEqual(events, {}) def test_trait_exception_handler_can_access_exception(self): """ Tests if trait exception handlers can access the traceback of the exception. """ import traceback from traits import trait_notifiers def _handle_exception(obj,name,old,new): self.assertIsNotNone(sys.exc_info()[0]) ge = GenerateFailingEvents() try: trait_notifiers.push_exception_handler( _handle_exception, reraise_exceptions=False, main=True ) ge.trait_set(name='John Cleese') finally: trait_notifiers.pop_exception_handler() class A(HasTraits): exception = Any foo = Event def foo_changed_handler(self): pass def foo_writer(a, stop_event): while not stop_event.is_set(): try: a.foo = True except Exception as e: a.exception = e class TestRaceCondition(unittest.TestCase): def setUp(self): push_exception_handler( handler=lambda *args: None, reraise_exceptions=True, main=True, ) def tearDown(self): pop_exception_handler() def test_listener_thread_safety(self): # Regression test for GitHub issue #56 a = A() stop_event = threading.Event() t = threading.Thread(target=foo_writer, args=(a, stop_event)) t.start() for _ in xrange(100): a.on_trait_change(a.foo_changed_handler, 'foo') time.sleep(0.0001) # encourage thread-switch a.on_trait_change(a.foo_changed_handler, 'foo', remove=True) stop_event.set() t.join() self.assertTrue(a.exception is None) def test_listener_deleted_race(self): # Regression test for exception that occurred when the listener_deleted # method is called after the dispose method on a # TraitsChangeNotifyWrapper. class SlowListener(HasTraits): def handle_age_change(self): time.sleep(1.0) def worker_thread(event_source, start_event): # Wait until the listener is set up on the main thread, then fire # the event. start_event.wait() event_source.age = 11 def main_thread(event_source, start_event): listener = SlowListener() event_source.on_trait_change(listener.handle_age_change, 'age') start_event.set() # Allow time to make sure that we're in the middle of handling an # event. time.sleep(0.5) event_source.on_trait_change( listener.handle_age_change, 'age', remove=True) # Previously, a ValueError would be raised on the worker thread # during (normal refcount-based) garbage collection. That # ValueError is ignored by the Python system, so the only # visible effect is the output to stderr. with captured_stderr() as s: start_event = threading.Event() event_source = GenerateEvents(age=10) t = threading.Thread( target=worker_thread, args=(event_source, start_event), ) t.start() main_thread(event_source, start_event) t.join() self.assertNotIn('Exception', s.getvalue()) # Run the unit tests (if invoked from the command line): if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_new_notifiers.py000066400000000000000000000027151300633736300217740ustar00rootroot00000000000000""" Tests for dynamic notifiers with `dispatch='new'`. Dynamic notifiers created with the `dispatch='new'` option dispatch event notifications on a new thread. The class handling the dispatch, `NewTraitChangeNotifyWrapper`, is a subclass of `TraitChangeNotifyWrapper`. Most of the functionality of the class is thus already covered by the `TestDynamicNotifiers` test case, and we only need to test that the notification really occurs on a separate thread. """ import thread import time from traits.api import Float, HasTraits from traits.testing.unittest_tools import unittest class Foo(HasTraits): foo = Float class TestNewNotifiers(unittest.TestCase): """ Tests for dynamic notifiers with `dispatch='new'`. """ def test_notification_on_separate_thread(self): notifications = [] def on_foo_notifications(obj, name, old, new): thread_id = thread.get_ident() event = (thread_id, obj, name, old, new) notifications.append(event) obj = Foo() obj.on_trait_change(on_foo_notifications, 'foo', dispatch='new') obj.foo = 3 # Wait for a while to make sure the notification has finished. time.sleep(0.1) self.assertEqual(len(notifications), 1) self.assertEqual(notifications[0][1:], (obj, 'foo', 0, 3)) this_thread_id = thread.get_ident() self.assertNotEqual(this_thread_id, notifications[0][0]) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_pickle_validated_dict.py000066400000000000000000000020151300633736300234010ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from cPickle import dumps, loads from traits.testing.unittest_tools import unittest from ..api import Dict, HasTraits, Int, List class C(HasTraits): # A dict trait containing a list trait a = Dict(Int, List(Int)) # And we must initialize it to something non-trivial def __init__(self): super(C, self).__init__() self.a = {1: [2, 3]} class PickleValidatedDictTestCase(unittest.TestCase): def test(self): # And we must unpickle one x = dumps(C()) try: loads(x) except AttributeError, e: self.fail('Unpickling raised an AttributeError: %s' % e) traits-4.6.0/traits/tests/test_property_delete.py000066400000000000000000000013321300633736300223210ustar00rootroot00000000000000""" Unit tests to ensure that we can call reset_traits/delete on a property trait (regression tests for Github issue #67). """ from traits import _py2to3 from traits.api import Any, HasTraits, Int, Property, TraitError from traits.testing.unittest_tools import unittest class E(HasTraits): a = Property(Any) b = Property(Int) class TestPropertyDelete(unittest.TestCase): def test_property_delete(self): e = E() with self.assertRaises(TraitError): del e.a with self.assertRaises(TraitError): del e.b def test_property_reset_traits(self): e = E() unresetable = e.reset_traits() _py2to3.assertCountEqual(self, unresetable, ['a', 'b']) traits-4.6.0/traits/tests/test_property_notifications.py000066400000000000000000000042301300633736300237300ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: David C. Morrill # Description: #------------------------------------------------------------------------------ from __future__ import absolute_import from ..api import HasTraits, Property class Test(HasTraits): __traits__ = {} def __value_get(self): return self.__dict__.get('_value', 0) def __value_set(self, value): old_value = self.__dict__.get('_value', 0) if value != old_value: self._value = value self.trait_property_changed('value', old_value, value) __traits__['value'] = Property(__value_get, __value_set) class Test_1 (Test): def value_changed(self, value): print 'value_changed:', value class Test_2 (Test): def anytrait_changed(self, name, value): print 'anytrait_changed for %s: %s' % (name, value) class Test_3 (Test_2): def value_changed(self, value): print 'value_changed:', value def on_value_changed(value): print 'on_value_changed:', value def on_anyvalue_changed(value): print 'on_anyvalue_changed:', value def test_property_notifications(): Test_1().value = 'test 1' Test_2().value = 'test 2' Test_3().value = 'test 3' test_4 = Test() test_4.on_trait_change(on_value_changed, 'value') test_4.value = 'test 4' test_5 = Test() test_5.on_trait_change(on_anyvalue_changed) test_5.value = 'test 5' test_6 = Test() test_6.on_trait_change(on_value_changed, 'value') test_6.on_trait_change(on_anyvalue_changed) test_6.value = 'test 6' test_7 = Test_3() test_7.on_trait_change(on_value_changed, 'value') test_7.on_trait_change(on_anyvalue_changed) test_7.value = 'test 7' traits-4.6.0/traits/tests/test_protocols_usage.py000066400000000000000000000232711300633736300223310ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Tests for protocols usage. """ from __future__ import absolute_import # Standard library imports. import os import pickle import sys from traits.testing.unittest_tools import unittest # Enthought library imports. from ..api import (Bool, HasTraits, Int, Interface, Str, Adapter, adapts, Property) # NOTE: There is a File class in apptools.io module, but since we want to # eliminate dependencies of Traits on other modules, we create another # minimal File class here to test the adapter implementation. # Test class class File(HasTraits): # The path name of this file/folder. path = Str # Is this an existing file? is_file = Property(Bool) # Is this an existing folder? is_folder = Property(Bool) def _get_is_file(self): """ Returns True if the path exists and is a file. """ return os.path.exists(self.path) and os.path.isfile(self.path) def _get_is_folder(self): """ Returns True if the path exists and is a folder. """ return os.path.exists(self.path) and os.path.isdir(self.path) # Test class. class Person(HasTraits): """ A person! """ name = Str age = Int @unittest.skipUnless(sys.version_info < (3,), "The 'adapts' and 'implements' class advisors " "are not supported in Python 3.") class ProtocolsUsageTestCase(unittest.TestCase): """ Tests for protocols usage. """ def test_adapts(self): """ adapts """ class IFoo(Interface): """ A simple interface. """ def foo(self): """ The only method for the IFoo interface. """ class Bar(HasTraits): """ A type that *doesn't* implement 'IFoo'. """ class BarToIFooAdapter(Adapter): """ Adapts from Bar to IFoo. """ adapts(Bar, to=IFoo) def foo(self): """ An implementation of the single method in the interface.""" return 'foo' b = Bar() # Make sure that the Bar instance can be adapted to 'IFoo'. self.assertNotEqual(None, IFoo(b)) self.assertEqual('foo', IFoo(b).foo()) def test_factory(self): """ factory """ class IInputStream(Interface): """ Fake interface for input stream. """ def get_input_stream(self): """ Get an input stream. """ def factory(obj): """ A factory for File to IInputStream adapters. """ if not obj.is_folder: adapter = FileToIInputStreamAdapter(adaptee=obj) else: adapter = None return adapter class FileToIInputStreamAdapter(Adapter): """ An adapter from 'File' to 'IInputStream'. """ adapts(File, to=IInputStream, factory=factory) ################################################################### # 'IInputStream' interface. ################################################################### def get_input_stream(self): """ Get an input stream. """ return file(self.adaptee.path, 'r') # Create a reference to this file cwd = os.path.dirname(os.path.abspath(__file__)) f = File(path=os.path.join(cwd, 'test_protocols_usage.py')) self.assertTrue(f.is_file) # A reference to the parent folder g = File(path='..') self.assertTrue(g.is_folder) # We should be able to adapt the file to an input stream... self.assertNotEqual(None, IInputStream(f, None)) # ... but not the folder. self.assertEqual(None, IInputStream(g, None)) # Make sure we can use the stream (this reads this module and makes # sure that it contains the right doc string). stream = IInputStream(f).get_input_stream() self.assertIn('"""' + __doc__, stream.read()) return def test_when_expression(self): """ when expression """ class IInputStream(Interface): """ Fake interface for input stream. """ def get_input_stream(self): """ Get an input stream. """ class FileToIInputStreamAdapter(Adapter): """ An adapter from 'File' to 'IInputStream'. """ adapts(File, to=IInputStream, when='not adaptee.is_folder') ################################################################### # 'IInputStream' interface. ################################################################### def get_input_stream(self): """ Get an input stream. """ return file(self.adaptee.path, 'r') # Create a reference to this file cwd = os.path.dirname(os.path.abspath(__file__)) f = File(path=os.path.join(cwd, 'test_protocols_usage.py')) self.assertTrue(f.is_file) # A reference to the parent folder g = File(path='..') self.assertTrue(g.is_folder) # We should be able to adapt the file to an input stream... self.assertNotEqual(None, IInputStream(f, None)) # ... but not the folder. self.assertEqual(None, IInputStream(g, None)) # Make sure we can use the stream (this reads this module and makes # sure that it contains the right doc string). stream = IInputStream(f).get_input_stream() self.assertIn('"""' + __doc__, stream.read()) return def test_cached(self): """ cached """ class ISaveable(Interface): """ Fake interface for saveable. """ # Is the object 'dirty'? dirty = Bool(False) def save(self, output_stream): """ Save the object to an output stream. """ class HasTraitsToISaveableAdapter(Adapter): """ An adapter from 'HasTraits' to 'ISaveable'. """ adapts(HasTraits, to=ISaveable, cached=True) #### 'ISaveable' interface ######################################## # Is the object 'dirty'? dirty = Bool(False) def save(self, output_stream): """ Save the object to an output stream. """ pickle.dump(self.adaptee, output_stream) self.dirty = False return #### Private interface ############################################ def _adaptee_changed(self, old, new): """ Static trait change handler. """ if old is not None: old.on_trait_change(self._set_dirty, remove=True) if new is not None: new.on_trait_change(self._set_dirty) self._set_dirty() return def _set_dirty(self): """ Sets the dirty flag to True. """ self.dirty = True return # Create some people! fred = Person(name='fred', age=42) wilma = Person(name='wilma', age=35) fred_saveable = ISaveable(fred) self.assertEqual(True, fred_saveable.dirty) wilma_saveable = ISaveable(wilma) self.assertEqual(True, wilma_saveable.dirty) # Make sure that Fred and Wilma have got their own saveable. self.assertNotEqual(id(fred_saveable), id(wilma_saveable)) # But make sure that their saveable's are cached. self.assertEqual(id(ISaveable(fred)), id(fred_saveable)) self.assertEqual(id(ISaveable(wilma)), id(wilma_saveable)) # Save Fred and Wilma and make sure that the dirty flag is cleared. fred_saveable.save(file('fred.pickle', 'w')) self.assertEqual(False, ISaveable(fred).dirty) wilma_saveable.save(file('wilma.pickle', 'w')) self.assertEqual(False, ISaveable(wilma).dirty) # Clean up. for path in ['fred.pickle', 'wilma.pickle']: if os.access(path, os.W_OK): os.remove(path) return def test_multiple_factories_for_type(self): """ multiple factories for type """ # There was a bug that prevented more than one adapter factory being # registered for the same class. class IFoo(Interface): pass class HasTraitsToIFooAdapter(Adapter): adapts(HasTraits, to=IFoo, cached=True) class IBar(Interface): pass class HasTraitsToIBarAdapter(Adapter): adapts(HasTraits, to=IBar, cached=True) return def test_multiple_factories_for_interface(self): """ multiple factories for interfaces """ # There was a bug that prevented more than one adapter factory being # registered for the same class. This test just makes sure that it # still works for interfaces too! class IBaz(Interface): pass class IFoo(Interface): pass class IBazToIFooAdapter(Adapter): adapts(IBaz, to=IFoo, cached=True) class IBar(Interface): pass class IBazToIBarAdapter(Adapter): adapts(IBaz, to=IBar, cached=True) return # Run the unit tests (if invoked from the command line): if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_range.py000066400000000000000000000051261300633736300202140ustar00rootroot00000000000000# Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Int, Range, Str, TraitError class WithFloatRange(HasTraits): r = Range(0.0, 100.0) r_copied_on_change = Str _changed_handler_calls = Int def _r_changed(self, old, new): self._changed_handler_calls += 1 self.r_copied_on_change = str(self.r) if (self.r % 10) > 0: self.r += 10 - (self.r % 10) class WithLargeIntRange(HasTraits): r = Range(0, 1000) r_copied_on_change = Str _changed_handler_calls = Int def _r_changed(self, old, new): self._changed_handler_calls += 1 self.r_copied_on_change = str(self.r) if self.r > 100: self.r = 0 class WithDynamicRange(HasTraits): low = Int(0) high = Int(10) value = Int(3) r = Range(value='value', low='low', high='high', exclude_high=True) def _r_changed(self, old, new): self._changed_handler_calls += 1 class RangeTestCase(unittest.TestCase): def test_non_ui_events(self): obj = WithFloatRange() obj._changed_handler_calls = 0 obj.r = 10 self.assertEqual(1, obj._changed_handler_calls) obj._changed_handler_calls = 0 obj.r = 34.56 self.assertEqual(obj._changed_handler_calls, 2) self.assertEqual(obj.r, 40) def test_non_ui_int_events(self): # Even though the range is configured for 0..1000, the handler resets # the value to 0 when it exceeds 100. obj = WithLargeIntRange() obj._changed_handler_calls = 0 obj.r = 10 self.assertEqual(obj._changed_handler_calls, 1) self.assertEqual(obj.r, 10) obj.r = 100 self.assertEqual(obj._changed_handler_calls, 2) self.assertEqual(obj.r, 100) obj.r = 101 self.assertEqual(obj._changed_handler_calls, 4) self.assertEqual(obj.r, 0) def test_dynamic_events(self): obj = WithDynamicRange() obj._changed_handler_calls = 0 obj.r = 5 self.assertEqual(obj._changed_handler_calls, 1) self.assertEqual(obj.r, 5) with self.assertRaises(TraitError): obj.r = obj.high self.assertEqual(obj.r, 5) traits-4.6.0/traits/tests/test_regression.py000066400000000000000000000114161300633736300212770ustar00rootroot00000000000000""" General regression tests for a variety of bugs. """ import gc import sys from ..has_traits import HasTraits, Property, on_trait_change from ..trait_types import Bool, DelegatesTo, Instance, Int, List from ..testing.unittest_tools import unittest class Dummy(HasTraits): x = Int(10) def _create_subclass(): class Subclass(HasTraits): pass return Subclass class Dummy2(HasTraits): y = Int(20) dummy = Instance(Dummy) class DelegateMess(HasTraits): dummy1 = Instance(Dummy, args=()) dummy2 = Instance(Dummy2) y = DelegatesTo('dummy2') handler_called = Bool(False) def _dummy2_default(self): # Create `self.dummy1` return Dummy2(dummy=self.dummy1) @on_trait_change('dummy1.x') def _on_dummy1_x(self): self.handler_called = True def _init_trait_listeners(self): """ Force the DelegatesTo listener to hook up first to exercise the worst case. """ for name in ['y', '_on_dummy1_x']: data = self.__class__.__listener_traits__[name] getattr(self, '_init_trait_%s_listener' % data[0])(name, *data) class DelegateLeak(HasTraits): visible = Property(Bool, depends_on='can_enable') can_enable = DelegatesTo('flag', prefix='x') flag = Instance(Dummy, kw={'x': 42}) class Presenter(HasTraits): obj = Instance(Dummy) y = Property(Int(), depends_on='obj.x') def _get_y(self): return self.obj.x class ListUpdatesTest(HasTraits): a = List b = List events_received = Int(0) @on_trait_change('a[], b[]') def _receive_events(self): self.events_received += 1 class TestRegression(unittest.TestCase): def test_default_value_for_no_cache(self): """ Make sure that CTrait.default_value_for() does not cache the result. """ dummy = Dummy() # Nothing in the __dict__ yet. self.assertEqual(dummy.__dict__, {}) ctrait = dummy.trait('x') default = ctrait.default_value_for(dummy, 'x') self.assertEqual(default, 10) self.assertEqual(dummy.__dict__, {}) def test_subclasses_weakref(self): """ Make sure that dynamically created subclasses are not held strongly by HasTraits. """ previous_subclasses = HasTraits.__subclasses__() _create_subclass() _create_subclass() _create_subclass() _create_subclass() gc.collect() self.assertEqual(previous_subclasses, HasTraits.__subclasses__()) def test_leaked_property_tuple(self): """ the property ctrait constructor shouldn't leak a tuple. """ class A(HasTraits): prop = Property() a = A() self.assertEqual(sys.getrefcount(a.trait('prop').property()), 1) def test_delegate_initializer(self): mess = DelegateMess() self.assertFalse(mess.handler_called) mess.dummy1.x = 20 self.assertTrue(mess.handler_called) def test_no_leaking_notifiers(self): """ Extended trait change notifications should not leaf TraitChangeNotifyWrappers. """ dummy = Dummy() ctrait = dummy._trait('x', 2) self.assertEqual(len(ctrait._notifiers(1)), 0) presenter = Presenter(obj=dummy) self.assertEqual(len(ctrait._notifiers(1)), 1) del presenter self.assertEqual(len(ctrait._notifiers(1)), 0) def test_init_list_depends(self): """ Using two lists with bracket notation in extended name notation should not raise an error. """ list_test = ListUpdatesTest() # Updates to list items and the list trait itself should be counted. list_test.a.append(0) list_test.b = [1, 2, 3] list_test.b[0] = 0 self.assertEqual(list_test.events_received, 3) def test_has_traits_notifiers_refleak(self): # Regression test for issue described in # https://github.com/enthought/traits/pull/248 warmup = 5 cycles = 10 counts = [] def handler(): pass def f(): obj = HasTraits() obj.on_trait_change(handler) # Warmup. for _ in xrange(cycles): f() gc.collect() counts.append(len(gc.get_objects())) # All the counts beyond the warmup period should be the same. self.assertEqual(counts[warmup:-1], counts[warmup+1:]) def test_delegation_refleak(self): warmup = 5 cycles = 10 counts = [] for _ in xrange(cycles): DelegateLeak() gc.collect() counts.append(len(gc.get_objects())) # All the counts should be the same. self.assertEqual(counts[warmup:-1], counts[warmup+1:]) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_rich_compare.py000066400000000000000000000127771300633736300215650ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Any, Str class IdentityCompare(HasTraits): bar = Any(rich_compare=False) class RichCompare(HasTraits): bar = Any(rich_compare=True) class RichCompareTests: def bar_changed(self, object, trait, old, new): self.changed_object = object self.changed_trait = trait self.changed_old = old self.changed_new = new self.changed_count += 1 def reset_change_tracker(self): self.changed_object = None self.changed_trait = None self.changed_old = None self.changed_new = None self.changed_count = 0 def check_tracker(self, object, trait, old, new, count): self.assertEqual(count, self.changed_count) self.assertIs(object, self.changed_object) self.assertEqual(trait, self.changed_trait) self.assertIs(old, self.changed_old) self.assertIs(new, self.changed_new) return def test_id_first_assignment(self): ic = IdentityCompare() ic.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = ic.bar ic.bar = self.a self.check_tracker(ic, 'bar', default_value, self.a, 1) return def test_rich_first_assignment(self): rich = RichCompare() rich.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = rich.bar rich.bar = self.a self.check_tracker(rich, 'bar', default_value, self.a, 1) return def test_id_same_object(self): ic = IdentityCompare() ic.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = ic.bar ic.bar = self.a self.check_tracker(ic, 'bar', default_value, self.a, 1) ic.bar = self.a self.check_tracker(ic, 'bar', default_value, self.a, 1) return def test_rich_same_object(self): rich = RichCompare() rich.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = rich.bar rich.bar = self.a self.check_tracker(rich, 'bar', default_value, self.a, 1) rich.bar = self.a self.check_tracker(rich, 'bar', default_value, self.a, 1) return def test_id_different_object(self): ic = IdentityCompare() ic.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = ic.bar ic.bar = self.a self.check_tracker(ic, 'bar', default_value, self.a, 1) ic.bar = self.different_from_a self.check_tracker(ic, 'bar', self.a, self.different_from_a, 2) return def test_rich_different_object(self): rich = RichCompare() rich.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = rich.bar rich.bar = self.a self.check_tracker(rich, 'bar', default_value, self.a, 1) rich.bar = self.different_from_a self.check_tracker(rich, 'bar', self.a, self.different_from_a, 2) return def test_id_different_object_same_as(self): ic = IdentityCompare() ic.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = ic.bar ic.bar = self.a self.check_tracker(ic, 'bar', default_value, self.a, 1) ic.bar = self.same_as_a self.check_tracker(ic, 'bar', self.a, self.same_as_a, 2) return def test_rich_different_object_same_as(self): rich = RichCompare() rich.on_trait_change(self.bar_changed, 'bar') self.reset_change_tracker() default_value = rich.bar rich.bar = self.a self.check_tracker(rich, 'bar', default_value, self.a, 1) # Values of a and same_as_a are the same and should therefore not # be considered a change. rich.bar = self.same_as_a self.check_tracker(rich, 'bar', default_value, self.a, 1) return class Foo(HasTraits): name = Str def __ne__(self, other): # Traits uses != to do the rich compare. The default implementation # of __ne__ is to compare the object identities. return self.name != other.name def __eq__(self, other): # Not required, but a good idea to make __eq__ and __ne__ compatible return self.name == other.name class RichCompareHasTraitsTestCase(unittest.TestCase, RichCompareTests): def setUp(self): self.a = Foo(name='a') self.same_as_a = Foo(name='a') self.different_from_a = Foo(name='not a') return def test_assumptions(self): self.assertIsNot(self.a, self.same_as_a) self.assertIsNot(self.a, self.different_from_a) self.assertEqual(self.a.name, self.same_as_a.name) self.assertNotEqual(self.a.name, self.different_from_a.name) return ### EOF traits-4.6.0/traits/tests/test_special_event_handlers.py000066400000000000000000000015751300633736300236250ustar00rootroot00000000000000from traits.testing.unittest_tools import unittest from traits.api import Any, HasStrictTraits, Str class TestSpecialEvent(unittest.TestCase): """ Test demonstrating special change events using the 'event' metadata. """ def setUp(self): self.change_events = [] self.foo = Foo(test=self) def test_events(self): self.foo.val = 'CHANGE' values = ['CHANGE'] self.assertEqual(self.change_events, values) def test_instance_events(self): foo = self.foo foo.add_trait('val2', Str(event='the_trait')) foo.val2 = 'CHANGE2' values = ['CHANGE2'] self.assertEqual(self.change_events, values) class Foo(HasStrictTraits): val = Str(event='the_trait') test = Any(None) def _the_trait_changed(self, new): if self.test is not None: self.test.change_events.append(new) traits-4.6.0/traits/tests/test_static_notifiers.py000066400000000000000000000063651300633736300224770ustar00rootroot00000000000000############################################################################## # Copyright 2014 Enthought, Inc. ############################################################################## """ Tests for the static notifiers. """ from traits.api import Float, HasTraits from traits.testing.unittest_tools import unittest from traits import trait_notifiers calls_0 = [] class StaticNotifiers0(HasTraits): ok = Float def _ok_changed(): calls_0.append(True) fail = Float def _fail_changed(): raise Exception('error') class StaticNotifiers1(HasTraits): ok = Float def _ok_changed(self): if not hasattr(self, 'calls'): self.calls = [] self.calls.append(True) fail = Float def _fail_changed(self): raise Exception('error') class StaticNotifiers2(HasTraits): ok = Float def _ok_changed(self, new): if not hasattr(self, 'calls'): self.calls = [] self.calls.append(new) fail = Float def _fail_changed(self, new): raise Exception('error') class StaticNotifiers3(HasTraits): ok = Float def _ok_changed(self, old, new): if not hasattr(self, 'calls'): self.calls = [] self.calls.append((old, new)) fail = Float def _fail_changed(self, old, new): raise Exception('error') class StaticNotifiers4(HasTraits): ok = Float def _ok_changed(self, name, old, new): if not hasattr(self, 'calls'): self.calls = [] self.calls.append((name, old, new)) fail = Float def _fail_changed(self, name, old, new): raise Exception('error') class TestNotifiers(unittest.TestCase): """ Tests for the static notifiers, and the "anytrait" static notifiers. """ def setUp(self): self.exceptions = [] trait_notifiers.push_exception_handler(self._handle_exception) def tearDown(self): trait_notifiers.pop_exception_handler() def _handle_exception(self, obj, name, old, new): self.exceptions.append((obj, name, old, new)) def test_static_notifiers_0(self): obj = StaticNotifiers0(ok=2) obj.ok = 3 self.assertEqual(len(calls_0), 2) obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_static_notifiers_1(self): obj = StaticNotifiers1(ok=2) obj.ok = 3 self.assertEqual(len(obj.calls), 2) obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_static_notifiers_2(self): obj = StaticNotifiers2(ok=2) obj.ok = 3 self.assertEqual(obj.calls, [2, 3]) obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_static_notifiers_3(self): obj = StaticNotifiers3(ok=2) obj.ok = 3 self.assertEqual(obj.calls, [(0, 2), (2, 3)]) obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) def test_static_notifiers_4(self): obj = StaticNotifiers4(ok=2) obj.ok = 3 self.assertEqual(obj.calls, [('ok', 0, 2), ('ok', 2, 3)]) obj.fail = 1 self.assertEqual(self.exceptions, [(obj, 'fail', 0, 1)]) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_str_handler.py000066400000000000000000000042321300633736300214220ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Trait, TraitError, TraitHandler from ..trait_base import strx # Validation via function def validator(object, name, value): if isinstance(value, basestring): # arbitrary rule for testing if value.find('fail') < 0: return value else: raise TraitError else: raise TraitError # Validation via Handler class MyHandler(TraitHandler): def validate(self, object, name, value): #print 'myvalidate "%s" %s' % (value, type(value)) try: value = strx(value) if value.find('fail') < 0: return value except: pass self.error(object, name, value) return def info(self): msg = "a string not containing the character sequence 'fail'" return msg class Foo(HasTraits): s = Trait('', validator) class Bar(HasTraits): s = Trait('', MyHandler()) class StrHandlerCase(unittest.TestCase): def test_validator_function(self): f = Foo() self.assertEqual(f.s, '') f.s = 'ok' self.assertEqual(f.s, 'ok') self.assertRaises(TraitError, setattr, f, 's', 'should fail.') self.assertEqual(f.s, 'ok') return def test_validator_handler(self): b = Bar() self.assertEqual(b.s, '') b.s = 'ok' self.assertEqual(b.s, 'ok') self.assertRaises(TraitError, setattr, b, 's', 'should fail.') self.assertEqual(b.s, 'ok') return ### EOF traits-4.6.0/traits/tests/test_string.py000066400000000000000000000022421300633736300204220ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2015, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Tests for the String trait type. """ try: import numpy except ImportError: numpy_available = False else: numpy_available = True from traits.testing.unittest_tools import unittest from ..api import HasTraits, String class A(HasTraits): string = String class TestString(unittest.TestCase): @unittest.skipUnless(numpy_available, "numpy not available") def test_accepts_numpy_string(self): numpy_string = numpy.str_("this is a numpy string!") a = A() a.string = numpy_string self.assertEqual(a.string, numpy_string) self.assertIs(type(a.string), str) traits-4.6.0/traits/tests/test_sync_traits.py000066400000000000000000000076131300633736300214650ustar00rootroot00000000000000""" Test that the `sync_trait` member function of `HasTraits` instances functions correctly. """ from __future__ import absolute_import from traits.testing.unittest_tools import unittest, UnittestTools from ..api import ( HasTraits, Int, List, push_exception_handler, pop_exception_handler) class A(HasTraits): t = Int l = List(Int) class B(HasTraits): t = Int u = Int l = List(Int) class TestSyncTraits(unittest.TestCase, UnittestTools): def setUp(self): push_exception_handler(lambda *args: None, reraise_exceptions=True) def tearDown(self): pop_exception_handler() def test_mutual_sync(self): """ Test that two traits can be mutually synchronized. """ a = A() b = B() a.sync_trait('t', b) b.t = 10 self.assertEqual(a.t, b.t) a.t = 20 self.assertEqual(b.t, a.t) # Check that we can remove the synchronization a.sync_trait('t', b, remove=True) with self.assertTraitDoesNotChange(a, 't'): b.t = 5 with self.assertTraitDoesNotChange(b, 't'): a.t = 7 def test_sync_alias(self): """ Test synchronization of a trait with an aliased trait. """ a = A() b = B() a.sync_trait('t', b, 'u') with self.assertTraitDoesNotChange(b, 't'): a.t = 5 self.assertEqual(a.t, b.u) b.u = 7 self.assertEqual(a.t, b.u) def test_one_way_sync(self): """ Test one-way synchronization of two traits. """ a = A(t=3) b = B(t=4) a.sync_trait('t', b, mutual=False) self.assertEqual(b.t, 3) a.t = 5 self.assertEqual(b.t, a.t) with self.assertTraitDoesNotChange(a, 't'): b.t = 7 # Remove synchronization a.sync_trait('t', b, remove=True) with self.assertTraitDoesNotChange(b, 't'): a.t = 12 def test_sync_lists(self): """ Test synchronization of list traits. """ a = A() b = B() a.sync_trait('l', b) # Change entire list. a.l = [1, 2, 3] self.assertEqual(a.l, b.l) b.l = [4, 5] self.assertEqual(a.l, b.l) # Change list items. a.l = [7, 8, 9] with self.assertTraitChanges(b, 'l_items'): a.l[-1] = 20 self.assertEqual(b.l, [7, 8, 20]) # Remove synchronization a.sync_trait('l', b, remove=True) with self.assertTraitDoesNotChange(a, 'l'): b.l = [7, 8] def test_sync_delete(self): """ Test that deleting a synchronized trait works. """ a = A() b = B() a.sync_trait('t', b) a.t = 5 del a try: # Updating `b.t` should not raise an exception due to remaining # listeners. b.t = 7 except Exception: self.fail("Unexpected exception while setting sync trait.") def test_sync_delete_one_way(self): """ Test that deleting a one-way synchronized trait works. (Regression test for #131). """ a = A() b = B() a.sync_trait('t', b, mutual=False) del b try: a.t = 42 except Exception: self.fail("Unexpected exception while setting sync trait.") def test_sync_ref_cycle(self): """ Regression test for #69. """ a = A() b = B() change_counter = [0] def _handle_change(): change_counter[0] += 1 b.on_trait_change(_handle_change, 't') a.sync_trait('t', b) a.t = 17 self.assertEqual(change_counter, [1]) # Delete b and check that no more changes to b.t are recorded. del b a.t = 42 self.assertEqual(change_counter, [1]) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_target.py000066400000000000000000000043611300633736300204060ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2010, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Test whether HasTraits objects with cycles can be garbage collected. """ # Standard library imports from traits.testing.unittest_tools import unittest # Enthought library imports from traits.api import HasTraits, Instance, Int class TestCase(unittest.TestCase): """ Tests the 'target' argument for on_traits_change. """ def test_simple(self): """ Tests a simple dynamic trait change handler. """ class Test(HasTraits): i = Int # Create objects obj = Test() target = HasTraits() # Set up to count changes in i self.count = 0 def count_notifies(): self.count += 1 obj.on_trait_change(count_notifies, "i", target=target) # Change the trait obj.i = 10 # Delete the target and change it again del target obj.i = 0 # The count should be 1 self.assertEqual(self.count, 1) def test_extended(self): """ Tests a dynamic trait change handler using extended names. """ class Child(HasTraits): i = Int class Parent(HasTraits): child = Instance(Child) # Create objects parent = Parent(child=Child()) target = HasTraits() # Set up to count changes in i self.count = 0 def count_notifies(): self.count += 1 parent.on_trait_change(count_notifies, "child:i", target=target) # Change the trait parent.child.i = 10 # Delete the target and change it again del target parent.child.i = 0 # The count should be 1 self.assertEqual(self.count, 1) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_trait_change_event_tracer.py000066400000000000000000000120731300633736300243100ustar00rootroot00000000000000""" Tests for the trait change event tracer. """ from traits.api import Float, HasTraits, on_trait_change from traits.testing.unittest_tools import unittest from traits import trait_notifiers class FuzException(Exception): pass class Foo(HasTraits): """ Test traits class with static and dynamic listeners. Changing `baz` triggers a dynamic listeners that modifies `bar`, which triggers one dynamic and one static listeners. """ bar = Float baz = Float fuz = Float def _bar_changed(self): pass @on_trait_change('bar') def _on_bar_change_notification(self): pass @on_trait_change('baz') def _on_baz_change_notification(self): self.bar += 1 @on_trait_change('fuz') def _on_fuz_change_notification(self): self.bar += 1 raise FuzException('method') class TestChangeEventTracers(unittest.TestCase): #### 'TestCase' protocol ################################################## def setUp(self): self.pre_change_events = [] self.post_change_events = [] self.exceptions = [] trait_notifiers.push_exception_handler( lambda obj, name, old, new: None ) def tearDown(self): trait_notifiers.pop_exception_handler() #### Private protocol ##################################################### def _collect_pre_notification_events(self, *args): self.pre_change_events.append(args) def _collect_post_notification_events(self, *args, **kwargs): self.post_change_events.append(args) self.exceptions.extend(kwargs.values()) #### Tests ################################################################ def test_change_event_hooks(self): # Create the test object and a function listener. foo = Foo() def _on_foo_baz_changed(obj, name, old, new): pass foo.on_trait_change(_on_foo_baz_changed, 'baz') # Set the event tracer and trigger a cascade of change events. trait_notifiers.set_change_event_tracers( pre_tracer=self._collect_pre_notification_events, post_tracer=self._collect_post_notification_events) foo.baz = 3 self.assertEqual(len(self.pre_change_events), 4) self.assertEqual(len(self.post_change_events), 4) expected_pre_events = [ (foo, 'baz', 0.0, 3.0, foo._on_baz_change_notification), (foo, 'bar', 0.0, 1.0, foo._bar_changed.im_func), (foo, 'bar', 0.0, 1.0, foo._on_bar_change_notification), (foo, 'baz', 0.0, 3.0, _on_foo_baz_changed), ] self.assertEqual(self.pre_change_events, expected_pre_events) expected_post_events = [ (foo, 'bar', 0.0, 1.0, foo._bar_changed.im_func), (foo, 'bar', 0.0, 1.0, foo._on_bar_change_notification), (foo, 'baz', 0.0, 3.0, foo._on_baz_change_notification), (foo, 'baz', 0.0, 3.0, _on_foo_baz_changed), ] self.assertEqual(self.post_change_events, expected_post_events) self.assertEqual(self.exceptions, [None] * 4) # Deactivate the tracer; it should not be called anymore. trait_notifiers.clear_change_event_tracers() foo.baz = 23 self.assertEqual(len(self.pre_change_events), 4) self.assertEqual(len(self.post_change_events), 4) def test_change_event_hooks_after_exception(self): # Create the test object and a function listener. foo = Foo() def _on_foo_fuz_changed(obj, name, old, new): raise FuzException('function') foo.on_trait_change(_on_foo_fuz_changed, 'fuz') # Set the event tracer and trigger a cascade of change events. trait_notifiers.set_change_event_tracers( pre_tracer=self._collect_pre_notification_events, post_tracer=self._collect_post_notification_events) foo.fuz = 3 self.assertEqual(len(self.pre_change_events), 4) self.assertEqual(len(self.post_change_events), 4) expected_pre_events = [ (foo, 'fuz', 0.0, 3.0, foo._on_fuz_change_notification), (foo, 'bar', 0.0, 1.0, foo._bar_changed.im_func), (foo, 'bar', 0.0, 1.0, foo._on_bar_change_notification), (foo, 'fuz', 0.0, 3.0, _on_foo_fuz_changed), ] self.assertEqual(self.pre_change_events, expected_pre_events) expected_post_events = [ (foo, 'bar', 0.0, 1.0, foo._bar_changed.im_func), (foo, 'bar', 0.0, 1.0, foo._on_bar_change_notification), (foo, 'fuz', 0.0, 3.0, foo._on_fuz_change_notification), (foo, 'fuz', 0.0, 3.0, _on_foo_fuz_changed), ] self.assertEqual(self.post_change_events, expected_post_events) self.assertEqual(self.exceptions[:2], [None, None]) self.assertIsInstance(self.exceptions[2], FuzException) self.assertEqual(self.exceptions[2].args, ('method',)) self.assertIsInstance(self.exceptions[3], FuzException) self.assertEqual(self.exceptions[3].args, ('function',)) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_trait_cycle.py000066400000000000000000000102201300633736300214110ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Test whether HasTraits objects with cycles can be garbage collected. """ from __future__ import absolute_import import gc import time from traits.testing.unittest_tools import unittest # Enthought library imports from ..api import HasTraits, Any, DelegatesTo, Instance, Int class TestCase(unittest.TestCase): def _simple_cycle_helper(self, foo_class): """ Can the garbage collector clean up a cycle with traits objects? """ # Create two Foo objects that refer to each other. first = foo_class() second = foo_class(child=first) first.child = second # get their ids foo_ids = [id(first), id(second)] # delete the items so that they can be garbage collected del first, second # tell the garbage collector to pick up the litter. gc.collect() # Now grab all objects in the process and ask for their ids all_ids = [id(obj) for obj in gc.get_objects()] # Ensure that neither of the Foo object ids are in this list for foo_id in foo_ids: self.assertTrue(foo_id not in all_ids) def test_simple_cycle_oldstyle_class(self): """ Can the garbage collector clean up a cycle with old style class? """ class Foo: def __init__(self, child=None): self.child = child self._simple_cycle_helper(Foo) def test_simple_cycle_newstyle_class(self): """ Can the garbage collector clean up a cycle with new style class? """ class Foo(object): def __init__(self, child=None): self.child = child self._simple_cycle_helper(Foo) def test_simple_cycle_hastraits(self): """ Can the garbage collector clean up a cycle with traits objects? """ class Foo(HasTraits): child = Any self._simple_cycle_helper(Foo) def test_reference_to_trait_dict(self): """ Does a HasTraits object refer to its __dict__ object? This test may point to why the previous one fails. Even if it doesn't, the functionality is needed for detecting problems with memory in debug.memory_tracker """ class Foo(HasTraits): child = Any foo = Foo() # It seems like foo sometimes has not finished construction yet, so # the frame found by referrers is not _exactly_ the same as Foo(). For # more information, see the gc doc: http://docs.python.org/lib/module- # gc.html # # The documentation says that this (get_referrers) should be used for # no purpose other than debugging, so this is really not a good way to # test the code. time.sleep(0.1) referrers = gc.get_referrers(foo.__dict__) self.assertTrue(len(referrers) > 0) self.assertTrue(foo in referrers) def test_delegates_to(self): """ Tests if an object that delegates to another is freed. """ class Base(HasTraits): """ Object we are delegating to. """ i = Int class Delegates(HasTraits): """ Object that delegates. """ b = Instance(Base) i = DelegatesTo('b') # Make a pair of object b = Base() d = Delegates(b=b) # Delete d and thoroughly collect garbage del d for i in range(3): gc.collect(2) # See if we still have a Delegates ds = [obj for obj in gc.get_objects() if isinstance(obj, Delegates)] self.assertEqual(ds, []) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_trait_default_initializer.py000066400000000000000000000032551300633736300243530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ from __future__ import absolute_import import unittest from ..trait_types import Int from ..has_traits import HasTraits class Foo(HasTraits): bar = Int def _bar_default(self): return 4 class TestTraitDefaultInitializer(unittest.TestCase): """ Test basic usage of the default method. """ def test_default_value(self): foo = Foo() self.assertEqual(foo.bar, 4) def test_default_value_override(self): foo = Foo(bar=3) self.assertEqual(foo.bar, 3) def test_reset_to_default(self): foo = Foo(bar=3) foo.reset_traits(traits=['bar']) self.assertEqual(foo.bar, 4) def test_error_propagation_in_default_methods(self): class FooException(Foo): def _bar_default(self): 1 / 0 foo = FooException() self.assertRaises(ZeroDivisionError, lambda: foo.bar) class FooKeyError(Foo): def _bar_default(self): raise KeyError() # Check that KeyError is propagated (issue #70). foo = FooKeyError() self.assertRaises(KeyError, lambda: foo.bar) traits-4.6.0/traits/tests/test_trait_exceptions.py000066400000000000000000000022721300633736300225030ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2015, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! #------------------------------------------------------------------------------ from traits.testing.unittest_tools import unittest from traits.api import HasTraits, Int class A(HasTraits): x = Int(5) class TestGetAttr(unittest.TestCase): def setUp(self): self.a = A() def test_bad__getattribute__(self): # Argument to __getattribute__ must be a string self.assertEqual(self.a.__getattribute__("x"), 5) with self.assertRaises(TypeError) as e: self.a.__getattribute__(2) # Error message contains value and type of bad attribute name exception_msg = str(e.exception) self.assertIn("2", exception_msg) self.assertIn("int", exception_msg) traits-4.6.0/traits/tests/test_trait_get_set.py000066400000000000000000000045441300633736300217600ustar00rootroot00000000000000# Test the 'trait_set', 'trait_get' interface to # the HasTraits class. # # Copyright (c) 2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # License included in /LICENSE.txt and may be redistributed only under the # conditions described in the aforementioned license. The license is also # available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! from __future__ import absolute_import from contextlib import contextmanager import logging import traits.has_traits from traits.testing.unittest_tools import unittest, UnittestTools from ..api import HasTraits, Str, Int class TraitsObject(HasTraits): string = Str integer = Int class ListHandler(logging.Handler): def __init__(self): logging.Handler.__init__(self) self.records = [] def emit(self, record): self.records.append(record) @contextmanager def catch_logs(module_name): logger = logging.getLogger(module_name) logger.propagate = False handler = ListHandler() logger.addHandler(handler) try: yield handler finally: logger.removeHandler(handler) logger.propagate = True class TestTraitGet(UnittestTools, unittest.TestCase): def test_trait_set(self): obj = TraitsObject() obj.trait_set(string='foo') self.assertEqual(obj.string, 'foo') self.assertEqual(obj.integer, 0) def test_trait_get(self): obj = TraitsObject() obj.trait_set(string='foo') values = obj.trait_get('string', 'integer') self.assertEqual(values, {'string': 'foo', 'integer': 0}) def test_trait_set_deprecated(self): obj = TraitsObject() with self.assertNotDeprecated(): obj.trait_set(integer=1) with self.assertDeprecated(): obj.set(string='foo') self.assertEqual(obj.string, 'foo') self.assertEqual(obj.integer, 1) def test_trait_get_deprecated(self): obj = TraitsObject() obj.string = 'foo' obj.integer = 1 with self.assertNotDeprecated(): values = obj.trait_get('integer') self.assertEqual(values, {'integer': 1}) with self.assertDeprecated(): values = obj.get('string') self.assertEqual(values, {'string': 'foo'}) traits-4.6.0/traits/tests/test_trait_list_dict.py000066400000000000000000000071301300633736300222760ustar00rootroot00000000000000############################################################################## # Copyright 2014 Enthought, Inc. ############################################################################## """ Test the persistence behavior of TraitListObjects, TraitDictObjects and TraitSetObjects. """ from __future__ import absolute_import import copy from cPickle import dumps, loads from ..has_traits import HasTraits, on_trait_change from ..trait_types import Dict, List, Set, Str, Int, Instance class A(HasTraits): alist = List(Int, range(5)) adict = Dict(Str, Int, dict(a=1, b=2)) aset = Set(Int, range(5)) events = List() @on_trait_change('alist_items,adict_items,aset_items') def _receive_events(self, object, name, old, new): self.events.append((name, new)) class B(HasTraits): dict = Dict(Str, Instance(A)) def test_trait_list_object_persists(): a = A() list = loads(dumps(a.alist)) assert list.object() is None list.append(10) assert len(a.events) == 0 a.alist.append(20) assert len(a.events) == 1 list2 = loads(dumps(list)) assert list2.object() is None def test_trait_dict_object_persists(): a = A() dict = loads(dumps(a.adict)) assert dict.object() is None dict['key'] = 10 assert len(a.events) == 0 a.adict['key'] = 10 assert len(a.events) == 1 dict2 = loads(dumps(dict)) assert dict2.object() is None def test_trait_set_object_persists(): a = A() set = loads(dumps(a.aset)) assert set.object() is None set.add(10) assert len(a.events) == 0 a.aset.add(20) assert len(a.events) == 1 set2 = loads(dumps(set)) assert set2.object() is None def test_trait_list_object_copies(): a = A() list = copy.deepcopy(a.alist) assert list.object() is None list.append(10) assert len(a.events) == 0 a.alist.append(20) assert len(a.events) == 1 list2 = copy.deepcopy(list) list2.append(30) assert list2.object() is None def test_trait_dict_object_copies(): a = A() dict = copy.deepcopy(a.adict) assert dict.object() is None dict['key'] = 10 assert len(a.events) == 0 a.adict['key'] = 10 assert len(a.events) == 1 dict2 = copy.deepcopy(dict) dict2['key2'] = 20 assert dict2.object() is None def test_trait_set_object_copies(): a = A() set1 = copy.deepcopy(a.aset) assert set1.object() is None set1.add(10) assert len(a.events) == 0 a.aset.add(20) assert len(a.events) == 1 set2 = copy.deepcopy(set1) set2.add(30) assert set2.object() is None set3 = a.aset.copy() assert type(set3) is set # Should not raise an AttributeError: set3.remove(20) def test_pickle_whole(): a = A() loads(dumps(a)) b = B(dict=dict(a=a)) loads(dumps(b)) def test_trait_set_object_operations(): # Regression test for update methods not coercing in the same way as # standard set objects (github issue #288) a = A() a.aset.update({10: 'a'}) assert a.aset == set([0, 1, 2, 3, 4, 10]) a.aset.intersection_update({3: 'b', 4: 'b', 10: 'a', 11: 'b'}) assert a.aset == set([3, 4, 10]) a.aset.difference_update({10: 'a', 11: 'b'}) assert a.aset == set([3, 4]) a.aset.symmetric_difference_update({10: 'a', 4: 'b'}) assert a.aset == set([3, 10]) def test_trait_set_object_inplace(): a = A() a.aset |= set([10]) assert a.aset == set([0, 1, 2, 3, 4, 10]) a.aset &= set([3, 4, 10, 11]) assert a.aset == set([3, 4, 10]) a.aset -= set([10, 11]) assert a.aset == set([3, 4]) a.aset ^= set([10, 4]) assert a.aset == set([3, 10]) traits-4.6.0/traits/tests/test_trait_types.py000066400000000000000000000037251300633736300214720ustar00rootroot00000000000000# Unit test case for testing trait types created by subclassing TraitType. # # Written by: David C. Morrill # # Date: 4/10/2007 # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # """ Unit test case for testing trait types created by subclassing TraitType. """ from traits.testing.unittest_tools import unittest from traits.api import Float, TraitType class TraitTypesTest(unittest.TestCase): def test_traits_shared_transient(self): # Regression test for a bug in traits where the same _metadata # dictionary was shared between different trait types. class LazyProperty(TraitType): def get(self, obj, name): return 1729 self.assertFalse(Float().transient) LazyProperty().as_ctrait() self.assertFalse(Float().transient) def test_numpy_validators_loaded_if_numpy_present(self): # If 'numpy' is available, the numpy validators should be loaded. # Make sure that numpy is present on this machine. try: import numpy except ImportError: self.skipTest("numpy library not found.") # Remove numpy from the list of imported modules. import sys del sys.modules['numpy'] for k in list(sys.modules): if k.startswith('numpy.'): del sys.modules[k] # Check that the validators contain the numpy types. from traits.trait_types import float_fast_validate import numpy self.assertIn(numpy.floating, float_fast_validate) # Run the unit tests (if invoked from the command line): if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_traits.py000066400000000000000000000665371300633736300204430ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: David C. Morrill Date: 03/20/2003 Description: Unit Test Case for the # Traits Package #------------------------------------------------------------------------------ # Imports from __future__ import absolute_import import sys from traits.testing.unittest_tools import unittest from ..api import (Any, Bytes, CBytes, CFloat, CInt, CLong, Delegate, Float, HasTraits, Instance, Int, List, Long, Str, Trait, TraitError, TraitList, TraitPrefixList, TraitPrefixMap, TraitRange, Tuple, pop_exception_handler, push_exception_handler) # Base unit test classes: class BaseTest(object): def assign(self, value): self.obj.value = value def coerce(self, value): return value def test_assignment(self): obj = self.obj # Validate default value value = self._default_value self.assertEqual(obj.value, value) # Validate all legal values for i, value in enumerate(self._good_values): obj.value = value self.assertEqual(obj.value, self.coerce(value)) # If there's a defined if i < len(self._mapped_values): self.assertEqual(obj.value_, self._mapped_values[i]) # Validate correct behavior for illegal values for value in self._bad_values: self.assertRaises(TraitError, self.assign, value) class test_base2(unittest.TestCase): def indexed_assign(self, list, index, value): list[index] = value def indexed_range_assign(self, list, index1, index2, value): list[index1: index2] = value def extended_slice_assign(self, list, index1, index2, step, value): list[index1:index2:step] = value # This avoids using a method name that contains 'test' so that this is not # called by the tester directly, as nose looks for all tests, regardless of # the handler at the bottom of this file. def check_values(self, name, default_value, good_values, bad_values, actual_values=None, mapped_values=None): obj = self.obj try: # Make sure the default value is correct: msg = 'default value' value = default_value self.assertEqual(getattr(obj, name), value) # Iterate over all legal values being tested: if actual_values is None: actual_values = good_values msg = 'legal values' i = 0 for value in good_values: setattr(obj, name, value) self.assertEqual(getattr(obj, name), actual_values[i]) if mapped_values is not None: self.assertEqual(getattr(obj, name + '_'), mapped_values[i]) i += 1 # Iterate over all illegal values being tested: msg = 'illegal values' for value in bad_values: self.assertRaises(TraitError, setattr, obj, name, value) except: print 'Failed while testing %s for value: %s(%s) in %s' % ( msg, value, value.__class__.__name__, self.__class__.__name__) raise class AnyTrait(HasTraits): value = Any class AnyTraitTest(BaseTest, unittest.TestCase): obj = AnyTrait() _default_value = None _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10}, (10,), None, 1j] _mapped_values = [] _bad_values = [] class CoercibleIntTrait(HasTraits): value = CInt(99) class IntTrait(HasTraits): value = Int(99) class CoercibleIntTest(AnyTraitTest): obj = CoercibleIntTrait() _default_value = 99 _good_values = [10, -10, 10L, -10L, 10.1, -10.1, '10', '-10', u'10', u'-10'] _bad_values = ['10L', '-10L', '10.1', '-10.1', u'10L', u'-10L', u'10.1', u'-10.1', 'ten', u'ten', [10], {'ten': 10}, (10, ), None, 1j] def coerce(self, value): try: return int(value) except: try: return int(float(value)) except: return int(long(value)) class IntTest(AnyTraitTest): obj = IntTrait() _default_value = 99 _good_values = [10, -10, 10L, -10L] _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L', u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10'] try: import numpy as np except ImportError: pass else: _good_values.extend([ np.int64(10), np.int64(-10), np.int32(10), np.int32(-10), np.int_(10), np.int_(-10) ]) def coerce(self, value): try: return int(value) except: try: return int(float(value)) except: return int(long(value)) class CoercibleLongTrait(HasTraits): value = CLong(99L) class LongTrait(HasTraits): value = Long(99L) class CoercibleLongTest(AnyTraitTest): obj = CoercibleLongTrait() _default_value = 99L _good_values = [ 10, -10, 10L, -10L, 10.1, -10.1, '10', '-10', u'10', u'-10'] if sys.version_info[0] < 3: _good_values.extend(['10L', '-10L', u'10L', u'-10L']) _bad_values = ['10.1', '-10.1', u'10.1', u'-10.1', 'ten', u'ten', [10], [10l], {'ten': 10}, (10,), (10L,), None, 1j] def coerce(self, value): try: return long(value) except: return long(float(value)) class LongTest(AnyTraitTest): obj = LongTrait() _default_value = 99L _good_values = [10, -10, 10L, -10L] _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10}, (10, ), (10L,), None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1', u'-10.1'] def coerce(self, value): try: return long(value) except: return long(float(value)) class CoercibleFloatTrait(HasTraits): value = CFloat(99.0) class FloatTrait(HasTraits): value = Float(99.0) class CoercibleFloatTest(AnyTraitTest): obj = CoercibleFloatTrait() _default_value = 99.0 _good_values = [10, -10, 10L, -10L, 10.1, -10.1, '10', '-10', '10.1', '-10.1', u'10', u'-10', u'10.1', u'-10.1'] _bad_values = ['10L', '-10L', u'10L', u'-10L', 'ten', u'ten', [10], {'ten': 10}, (10, ), None, 1j] def coerce(self, value): try: return float(value) except: return float(long(value)) class FloatTest(AnyTraitTest): obj = FloatTrait() _default_value = 99.0 _good_values = [10, -10, 10.1, -10.1] _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1', u'-10.1'] if sys.version_info[0] < 3: _good_values.extend([long(-10), long(10)]) def coerce(self, value): try: return float(value) except: return float(long(value)) # Trait that can only have 'complex'(i.e. imaginary) values: class ImaginaryValueTrait(HasTraits): value = Trait(99.0 - 99.0j) class ImaginaryValueTest(AnyTraitTest): obj = ImaginaryValueTrait() _default_value = 99.0 - 99.0j _good_values = [10, -10, 10L, -10L, 10.1, -10.1, '10', '-10', '10.1', '-10.1', 10j, 10 + 10j, 10 - 10j, 10.1j, 10.1 + 10.1j, 10.1 - 10.1j, '10j', '10+10j', '10-10j'] _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10}, (10,), None] def coerce(self, value): try: return complex(value) except: return complex(long(value)) class StringTrait(HasTraits): value = Trait('string') class StringTest(AnyTraitTest): obj = StringTrait() _default_value = 'string' _good_values = [10, -10, 10L, -10L, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', '-10.1', 'string', u'string', 1j, [10], ['ten'], {'ten': 10}, (10,), None] _bad_values = [] def coerce(self, value): return str(value) class UnicodeTrait(HasTraits): value = Trait(u'unicode') class UnicodeTest(StringTest): obj = UnicodeTrait() _default_value = u'unicode' _good_values = [10, -10, 10L, -10L, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', '-10.1', '', u'', 'string', u'string', 1j, [10], ['ten'], [u'ten'], {'ten': 10}, (10,), None] _bad_values = [] def coerce(self, value): return str(value) class BytesTrait(HasTraits): value = Bytes(b'bytes') version_dependent = ['', 'string'] class BytesTest(StringTest): obj = BytesTrait() _default_value = b'bytes' _good_values = [b'', b'10', b'-10'] + (version_dependent if sys.version_info[0] == 2 else []) _bad_values = [10, -10, 10L, 10.1, u'unicode', u'', [b''], [b'bytes'], [0], {b'ten': b'10'}, (b'',), None, True] + (version_dependent if sys.version_info[0] == 3 else []) def coerce(self, value): return bytes(value) class CoercibleBytesTrait(HasTraits): value = CBytes(b'bytes') version_dependent = [ '', 'string', u'unicode', u'', -10, 10.1, [b''], [b'bytes'], [-10], (-10,), {-10: 'foo'}, set([-10]), [256], (256,), {256: 'foo'}, set([256]), {b'ten': b'10'}, (b'',), None, ] class CoercibleBytesTest(StringTest): obj = CoercibleBytesTrait() _default_value = b'bytes' _good_values = [ b'', b'10', b'-10', 10, 10L, [10], (10,), set([10]), {10: 'foo'}, True] + (version_dependent if sys.version_info[0] == 2 else []) _bad_values = (version_dependent if sys.version_info[0] == 3 else []) def coerce(self, value): return bytes(value) class EnumTrait(HasTraits): value = Trait([1, 'one', 2, 'two', 3, 'three', 4.4, u'four.four']) class EnumTest(AnyTraitTest): obj = EnumTrait() _default_value = 1 _good_values = [1, 'one', 2, 'two', 3, 'three', 4.4, u'four.four'] _bad_values = [0, 'zero', 4, None] class MappedTrait(HasTraits): value = Trait('one', {'one': 1, 'two': 2, 'three': 3}) class MappedTest(AnyTraitTest): obj = MappedTrait() _default_value = 'one' _good_values = ['one', 'two', 'three'] _mapped_values = [1, 2, 3] _bad_values = ['four', 1, 2, 3, [1], (1,), {1: 1}, None] class PrefixListTrait(HasTraits): value = Trait('one', TraitPrefixList('one', 'two', 'three')) class PrefixListTest(AnyTraitTest): obj = PrefixListTrait() _default_value = 'one' _good_values = ['o', 'on', 'one', 'tw', 'two', 'th', 'thr', 'thre', 'three'] _bad_values = ['t', 'one ', ' two', 1, None] def coerce(self, value): return {'o': 'one', 'on': 'one', 'tw': 'two', 'th': 'three'}[value[:2]] class PrefixMapTrait(HasTraits): value = Trait('one', TraitPrefixMap({'one': 1, 'two': 2, 'three': 3})) class PrefixMapTest(AnyTraitTest): obj = PrefixMapTrait() _default_value = 'one' _good_values = ['o', 'on', 'one', 'tw', 'two', 'th', 'thr', 'thre', 'three'] _mapped_values = [1, 1, 1, 2, 2, 3, 3, 3] _bad_values = ['t', 'one ', ' two', 1, None] def coerce(self, value): return {'o': 'one', 'on': 'one', 'tw': 'two', 'th': 'three'}[value[:2]] class IntRangeTrait(HasTraits): value = Trait(3, TraitRange(2, 5)) class IntRangeTest(AnyTraitTest): obj = IntRangeTrait() _default_value = 3 _good_values = [2, 3, 4, 5] _bad_values = [0, 1, 6, 0.999, 6.01, 'two', '0.999', '6.01', None] def coerce(self, value): try: return int(value) except: try: return int(float(value)) except: return int(long(value)) class FloatRangeTrait(HasTraits): value = Trait(3.0, TraitRange(2.0, 5.0)) class FloatRangeTest(AnyTraitTest): obj = FloatRangeTrait() _default_value = 3.0 _good_values = [2.0, 3.0, 4.0, 5.0, 2.001, 4.999] _bad_values = [0, 1, 6, 0L, 1L, 6L, 1.999, 6.01, 'two', '0.999', '6.01', None] def coerce(self, value): try: return float(value) except: return float(long(value)) # Old style class version: class OTraitTest1: pass class OTraitTest2(OTraitTest1): pass class OTraitTest3(OTraitTest2): pass class OBadTraitTest: pass otrait_test1 = OTraitTest1() class OldInstanceTrait(HasTraits): value = Trait(otrait_test1) class OldInstanceTest(AnyTraitTest): obj = OldInstanceTrait() _default_value = otrait_test1 _good_values = [otrait_test1, OTraitTest1(), OTraitTest2(), OTraitTest3(), None] _bad_values = [0, 0L, 0.0, 0j, OTraitTest1, OTraitTest2, OBadTraitTest(), 'string', u'string', [otrait_test1], (otrait_test1,), {'data': otrait_test1}] # New style class version: class NTraitTest1(object): pass class NTraitTest2(NTraitTest1): pass class NTraitTest3(NTraitTest2): pass class NBadTraitTest: pass ntrait_test1 = NTraitTest1() class NewInstanceTrait(HasTraits): value = Trait(ntrait_test1) class NewInstanceTest(AnyTraitTest): obj = NewInstanceTrait() _default_value = ntrait_test1 _good_values = [ntrait_test1, NTraitTest1(), NTraitTest2(), NTraitTest3(), None] _bad_values = [0, 0L, 0.0, 0j, NTraitTest1, NTraitTest2, NBadTraitTest(), 'string', u'string', [ntrait_test1], (ntrait_test1,), {'data': ntrait_test1}] class FactoryClass(HasTraits): pass class ConsumerClass(HasTraits): x = Instance(FactoryClass, ()) class ConsumerSubclass(ConsumerClass): x = FactoryClass() embedded_instance_trait = Trait('', Str, Instance('traits.has_traits.HasTraits')) class Dummy(HasTraits): x = embedded_instance_trait xl = List(embedded_instance_trait) class RegressionTest(unittest.TestCase): """ Check that fixed bugs stay fixed. """ def test_factory_subclass_no_segfault(self): """ Test that we can provide an instance as a default in the definition of a subclass. """ # There used to be a bug where this would segfault. obj = ConsumerSubclass() obj.x def test_trait_compound_instance(self): """ Test that a deferred Instance() embedded in a TraitCompound handler and then a list will not replace the validate method for the outermost trait. """ # Pass through an instance in order to make the instance trait resolve # the class. d = Dummy() d.xl = [HasTraits()] d.x = 'OK' # Trait(using a function) that must be an odd integer: def odd_integer(object, name, value): try: float(value) if(value % 2) == 1: return int(value) except: pass raise TraitError class OddIntegerTrait(HasTraits): value = Trait(99, odd_integer) class OddIntegerTest(AnyTraitTest): obj = OddIntegerTrait() _default_value = 99 _good_values = [1, 3, 5, 7, 9, 999999999, 1L, 3L, 5L, 7L, 9L, 999999999L, 1.0, 3.0, 5.0, 7.0, 9.0, 999999999.0, -1, -3, -5, -7, -9, -999999999, -1L, -3L, -5L, -7L, -9L, -999999999L, -1.0, -3.0, -5.0, -7.0, -9.0, -999999999.0] _bad_values = [0, 2, -2, 1j, None, '1', [1], (1,), {1: 1}] class NotifierTraits(HasTraits): value1 = Int value2 = Int value1_count = Int value2_count = Int def _anytrait_changed(self, trait_name, old, new): if trait_name == 'value1': self.value1_count += 1 elif trait_name == 'value2': self.value2_count += 1 def _value1_changed(self, old, new): self.value1_count += 1 def _value2_changed(self, old, new): self.value2_count += 1 class NotifierTests(unittest.TestCase): obj = NotifierTraits() def __init__(self, value): unittest.TestCase.__init__(self, value) def setUp(self): obj = self.obj obj.value1 = 0 obj.value2 = 0 obj.value1_count = 0 obj.value2_count = 0 def tearDown(self): obj = self.obj obj.on_trait_change(self.on_value1_changed, 'value1', remove=True) obj.on_trait_change(self.on_value2_changed, 'value2', remove=True) obj.on_trait_change(self.on_anytrait_changed, remove=True) def on_anytrait_changed(self, object, trait_name, old, new): if trait_name == 'value1': self.obj.value1_count += 1 elif trait_name == 'value2': self.obj.value2_count += 1 def on_value1_changed(self): self.obj.value1_count += 1 def on_value2_changed(self): self.obj.value2_count += 1 def test_simple(self): obj = self.obj obj.value1 = 1 self.assertEqual(obj.value1_count, 2) self.assertEqual(obj.value2_count, 0) obj.value2 = 1 self.assertEqual(obj.value1_count, 2) self.assertEqual(obj.value2_count, 2) def test_complex(self): obj = self.obj obj.on_trait_change(self.on_value1_changed, 'value1') obj.value1 = 1 self.assertEqual(obj.value1_count, 3) self.assertEqual(obj.value2_count, 0) obj.on_trait_change(self.on_value2_changed, 'value2') obj.value2 = 1 self.assertEqual(obj.value1_count, 3) self.assertEqual(obj.value2_count, 3) obj.on_trait_change(self.on_anytrait_changed) obj.value1 = 2 self.assertEqual(obj.value1_count, 7) self.assertEqual(obj.value2_count, 3) obj.value1 = 2 self.assertEqual(obj.value1_count, 7) self.assertEqual(obj.value2_count, 3) obj.value2 = 2 self.assertEqual(obj.value1_count, 7) self.assertEqual(obj.value2_count, 7) obj.on_trait_change(self.on_value1_changed, 'value1', remove=True) obj.value1 = 3 self.assertEqual(obj.value1_count, 10) self.assertEqual(obj.value2_count, 7) obj.on_trait_change(self.on_value2_changed, 'value2', remove=True) obj.value2 = 3 self.assertEqual(obj.value1_count, 10) self.assertEqual(obj.value2_count, 10) obj.on_trait_change(self.on_anytrait_changed, remove=True) obj.value1 = 4 self.assertEqual(obj.value1_count, 12) self.assertEqual(obj.value2_count, 10) obj.value2 = 4 self.assertEqual(obj.value1_count, 12) self.assertEqual(obj.value2_count, 12) class RaisesArgumentlessRuntimeError(HasTraits): x = Int(0) def _x_changed(self): raise RuntimeError class TestRuntimeError(unittest.TestCase): def setUp(self): push_exception_handler(lambda *args: None, reraise_exceptions=True) def tearDown(self): pop_exception_handler() def test_runtime_error(self): f = RaisesArgumentlessRuntimeError() self.assertRaises(RuntimeError, setattr, f, 'x', 5) class DelegatedFloatTrait(HasTraits): value = Trait(99.0) class DelegateTrait(HasTraits): value = Delegate('delegate') delegate = Trait(DelegatedFloatTrait()) class DelegateTrait2(DelegateTrait): delegate = Trait(DelegateTrait()) class DelegateTrait3(DelegateTrait): delegate = Trait(DelegateTrait2()) class DelegateTests(unittest.TestCase): def test_delegation(self): obj = DelegateTrait3() self.assertEqual(obj.value, 99.0) parent1 = obj.delegate parent2 = parent1.delegate parent3 = parent2.delegate parent3.value = 3.0 self.assertEqual(obj.value, 3.0) parent2.value = 2.0 self.assertEqual(obj.value, 2.0) self.assertEqual(parent3.value, 3.0) parent1.value = 1.0 self.assertEqual(obj.value, 1.0) self.assertEqual(parent2.value, 2.0) self.assertEqual(parent3.value, 3.0) obj.value = 0.0 self.assertEqual(obj.value, 0.0) self.assertEqual(parent1.value, 1.0) self.assertEqual(parent2.value, 2.0) self.assertEqual(parent3.value, 3.0) del obj.value self.assertEqual(obj.value, 1.0) del parent1.value self.assertEqual(obj.value, 2.0) self.assertEqual(parent1.value, 2.0) del parent2.value self.assertEqual(obj.value, 3.0) self.assertEqual(parent1.value, 3.0) self.assertEqual(parent2.value, 3.0) del parent3.value # Uncommenting the following line allows # the last assertions to pass. However, this # may not be intended behavior, so keeping # the line commented. #del parent2.value self.assertEqual(obj.value, 99.0) self.assertEqual(parent1.value, 99.0) self.assertEqual(parent2.value, 99.0) self.assertEqual(parent3.value, 99.0) # Complex(i.e. 'composite') Traits tests: # Make a TraitCompound handler that does not have a fast_validate so we can # check for a particular regression. slow = Trait(1, TraitRange(1, 3), TraitRange(-3, -1)) try: del slow.handler.fast_validate except AttributeError: pass class complex_value(HasTraits): num1 = Trait(1, TraitRange(1, 5), TraitRange(-5, -1)) num2 = Trait(1, TraitRange(1, 5), TraitPrefixList('one', 'two', 'three', 'four', 'five')) num3 = Trait(1, TraitRange(1, 5), TraitPrefixMap({'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})) num4 = Trait(1, Trait(1, Tuple, slow), 10) num5 = Trait(1, 10, Trait(1, Tuple, slow)) class test_complex_value(test_base2): obj = complex_value() def test_num1(self): self.check_values('num1', 1, [1, 2, 3, 4, 5, -1, -2, -3, -4, -5], [0, 6, -6, '0', '6', '-6', 0.0, 6.0, -6.0, [1], (1,), {1: 1}, None], [1, 2, 3, 4, 5, -1, -2, -3, -4, -5]) def test_enum_exceptions(self): """ Check that enumerated values can be combined with nested TraitCompound handlers. """ self.check_values('num4', 1, [1, 2, 3, -3, -2, -1, 10, ()], [0, 4, 5, -5, -4, 11], ) self.check_values('num5', 1, [1, 2, 3, -3, -2, -1, 10, ()], [0, 4, 5, -5, -4, 11], ) class list_value(HasTraits): # Trait definitions: list1 = Trait([2], TraitList(Trait([1, 2, 3, 4]), maxlen=4)) list2 = Trait([2], TraitList(Trait([1, 2, 3, 4]), minlen=1, maxlen=4)) alist = List() class test_list_value(test_base2): obj = list_value() def setUp(self): test_base2.setUp(self) self.last_event = None def tearDown(self): del self.last_event def del_range(self, list, index1, index2): del list[index1: index2] def del_extended_slice(self, list, index1, index2, step): del list[index1:index2:step] def check_list(self, list): self.assertEqual(list, [2]) self.assertEqual(len(list), 1) list.append(3) self.assertEqual(len(list), 2) list[1] = 2 self.assertEqual(list[1], 2) self.assertEqual(len(list), 2) list[0] = 1 self.assertEqual(list[0], 1) self.assertEqual(len(list), 2) self.assertRaises(TraitError, self.indexed_assign, list, 0, 5) self.assertRaises(TraitError, list.append, 5) self.assertRaises(TraitError, list.extend, [1, 2, 3]) list.extend([3, 4]) self.assertEqual(list, [1, 2, 3, 4]) self.assertRaises(TraitError, list.append, 1) self.assertRaises(ValueError, self.extended_slice_assign, list, 0, 4, 2, [4, 5, 6]) del list[1] self.assertEqual(list, [1, 3, 4]) del list[0] self.assertEqual(list, [3, 4]) list[:0] = [1, 2] self.assertEqual(list, [1, 2, 3, 4]) self.assertRaises(TraitError, self.indexed_range_assign, list, 0, 0, [1]) del list[0:3] self.assertEqual(list, [4]) self.assertRaises(TraitError, self.indexed_range_assign, list, 0, 0, [4, 5]) def test_list1(self): self.check_list(self.obj.list1) def test_list2(self): self.check_list(self.obj.list2) self.assertRaises(TraitError, self.del_range, self.obj.list2, 0, 1) self.assertRaises(TraitError, self.del_extended_slice, self.obj.list2, 4, -5, -1) def assertLastTraitListEventEqual(self, index, removed, added): self.assertEqual(self.last_event.index, index) self.assertEqual(self.last_event.removed, removed) self.assertEqual(self.last_event.added, added) def test_trait_list_event(self): """ Record TraitListEvent behavior. """ # FIXME: The behavior of TraitListEvent is suboptimal with # respect to extended slice changes. Previously, TraitListObject # used to have a __setitem__() and a separate __setslice__() to # handle non-extended slices. Extended slices were added to the # underlying list object later. The __setitem__() code handled # the new extended slices, but created the TraitListEvent in the # same way it did for an integer index; namely it wrapped the # value with a list. For simple slices, the `index` attribute of # the TraitListEvent is an integer, and the `added` list is just # the list of values added. For an extended slice, the `index` # attribute is the slice object and the `added` list is the list # of values wrapped in another list. self.obj.alist = [1, 2, 3, 4] self.obj.on_trait_change(self._record_trait_list_event, 'alist_items') del self.obj.alist[0] self.assertLastTraitListEventEqual(0, [1], []) self.obj.alist.append(5) self.assertLastTraitListEventEqual(3, [], [5]) self.obj.alist[0:2] = [6, 7] self.assertLastTraitListEventEqual(0, [2, 3], [6, 7]) self.obj.alist[0:2:1] = [8, 9] self.assertLastTraitListEventEqual(0, [6, 7], [8, 9]) old_event = self.last_event self.obj.alist[0:2:1] = [8, 9] # If no values changed, no new TraitListEvent will be generated. self.assertIs(self.last_event, old_event) self.obj.alist[0:4:2] = [10, 11] self.assertLastTraitListEventEqual( slice(0, 4, 2), [[8, 4]], [[10, 11]]) del self.obj.alist[1:4:2] self.assertLastTraitListEventEqual(slice(1, 4, 2), [[9, 5]], []) self.obj.alist = [1, 2, 3, 4] del self.obj.alist[2:4] self.assertLastTraitListEventEqual(2, [3, 4], []) def _record_trait_list_event(self, object, name, old, new): self.last_event = new traits-4.6.0/traits/tests/test_tuple.py000066400000000000000000000004511300633736300202450ustar00rootroot00000000000000""" Unit tests for the Tuple trait type. """ from traits.testing.unittest_tools import unittest from traits.tests.tuple_test_mixin import TupleTestMixin from traits.trait_types import Tuple class TupleTestCase(TupleTestMixin, unittest.TestCase): def setUp(self): self.trait = Tuple traits-4.6.0/traits/tests/test_ui_notifiers.py000066400000000000000000000100011300633736300216030ustar00rootroot00000000000000""" Tests for dynamic notifiers with `dispatch='ui'`. Dynamic notifiers created with the `dispatch='ui'` option dispatch event notifications on the UI thread. The class handling the dispatch, `FastUITraitChangeNotifyWrapper`, is a subclass of `TraitChangeNotifyWrapper`. Most of the functionality of the class is thus already covered by the `TestDynamicNotifiers` test case, and we only need to test that the notification really occurs on the UI thread. At present, `dispatch='ui'` and `dispatch='fast_ui'` have the same effect. """ # Preamble: Try importing Qt, and set QT_FOUND to True on success. try: from pyface.util.guisupport import get_app_qt4, start_event_loop_qt4 # This import is necessary to set the `ui_handler` global variable in # `traits.trait_notifiers`, which is responsible for dispatching the events # to the UI thread. from traitsui.qt4 import toolkit qt4_app = get_app_qt4() except Exception: QT_FOUND = False else: QT_FOUND = True import thread from threading import Thread import time from traits.api import Callable, Float, HasTraits, on_trait_change from traits.testing.unittest_tools import unittest from traits import trait_notifiers class CalledAsMethod(HasTraits): foo = Float class CalledAsDecorator(HasTraits): foo = Float callback = Callable @on_trait_change('foo', dispatch='ui') def on_foo_change(self, obj, name, old, new): self.callback(obj, name, old, new) class BaseTestUINotifiers(object): """ Tests for dynamic notifiers with `dispatch='ui'`. """ #### 'TestCase' protocol ################################################## def setUp(self): self.notifications = [] #### 'TestUINotifiers' protocol ########################################### def flush_event_loop(self): """ Post and process the Qt events. """ qt4_app.sendPostedEvents() qt4_app.processEvents() def on_foo_notifications(self, obj, name, old, new): thread_id = thread.get_ident() event = (thread_id, (obj, name, old, new)) self.notifications.append(event) #### Tests ################################################################ @unittest.skipIf( not QT_FOUND, "Qt event loop not found, UI dispatch not possible.") def test_notification_from_main_thread(self): obj = self.obj_factory() obj.foo = 3 self.flush_event_loop() notifications = self.notifications self.assertEqual(len(notifications), 1) thread_id, event = notifications[0] self.assertEqual(event, (obj, 'foo', 0, 3)) ui_thread = trait_notifiers.ui_thread self.assertEqual(thread_id, ui_thread) @unittest.skipIf( not QT_FOUND, "Qt event loop not found, UI dispatch not possible.") def test_notification_from_separate_thread(self): obj = self.obj_factory() # Set obj.foo to 3 on a separate thread. def set_foo_to_3(obj): obj.foo = 3 Thread(target=set_foo_to_3, args=(obj,)).start() # Wait for a while to make sure the function has finished. time.sleep(0.1) self.flush_event_loop() notifications = self.notifications self.assertEqual(len(notifications), 1) thread_id, event = notifications[0] self.assertEqual(event, (obj, 'foo', 0, 3)) ui_thread = trait_notifiers.ui_thread self.assertEqual(thread_id, ui_thread) class TestMethodUINotifiers(BaseTestUINotifiers, unittest.TestCase): """ Tests for dynamic notifiers with `dispatch='ui'` set by method call. """ def obj_factory(self): obj = CalledAsMethod() obj.on_trait_change(self.on_foo_notifications, 'foo', dispatch='ui') return obj class TestDecoratorUINotifiers(BaseTestUINotifiers, unittest.TestCase): """ Tests for dynamic notifiers with `dispatch='ui'` set by decorator. """ def obj_factory(self): return CalledAsDecorator(callback=self.on_foo_notifications) if __name__ == '__main__': unittest.main() traits-4.6.0/traits/tests/test_undefined.py000066400000000000000000000040721300633736300210600ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ from __future__ import absolute_import from traits.testing.unittest_tools import unittest from ..api import HasTraits, Str, Undefined, ReadOnly, Float class Foo(HasTraits): name = Str() original_name = ReadOnly bar = Str baz = Float def _name_changed(self): if self.original_name is Undefined: self.original_name = self.name class Bar(HasTraits): name = Str(Undefined) class UndefinedTestCase(unittest.TestCase): def test_initial_value(self): b = Bar() self.assertEqual(b.name, Undefined) return def test_name_change(self): b = Bar() b.name = 'first' self.assertEqual(b.name, 'first') return def test_read_only_write_once(self): f = Foo() self.assertEqual(f.name, '') self.assertIs(f.original_name, Undefined) f.name = 'first' self.assertEqual(f.name, 'first') self.assertEqual(f.original_name, 'first') f.name = 'second' self.assertEqual(f.name, 'second') self.assertEqual(f.original_name, 'first') return def test_read_only_write_once_from_constructor(self): f = Foo(name='first') f.name = 'first' self.assertEqual(f.name, 'first') self.assertEqual(f.original_name, 'first') f.name = 'second' self.assertEqual(f.name, 'second') self.assertEqual(f.original_name, 'first') return ### EOF ####################################################################### traits-4.6.0/traits/tests/test_validated_tuple.py000066400000000000000000000034041300633736300222630ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # # Copyright (c) 2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # ----------------------------------------------------------------------------- from traits.api import HasStrictTraits, Int, TraitError from traits.testing.unittest_tools import unittest from traits.tests.tuple_test_mixin import TupleTestMixin from traits.trait_types import ValidatedTuple class Simple(HasStrictTraits): scalar_range = ValidatedTuple( Int(0), Int(1), fvalidate=lambda x: x[0] < x[1]) class ValidatedTupleTestCase(TupleTestMixin, unittest.TestCase): def setUp(self): self.trait = ValidatedTuple def test_initialization(self): simple = Simple() self.assertEqual(simple.scalar_range, (0, 1)) def test_custom_validation(self): simple = Simple() simple.scalar_range = (2, 5) self.assertEqual(simple.scalar_range, (2, 5)) with self.assertRaises(TraitError): simple.scalar_range = (5, 2) def test_error_during_custom_validation(self): def fvalidate(x): if x == (5, 2): raise RuntimeError() return True class Simple(HasStrictTraits): scalar_range = ValidatedTuple(Int(0), Int(1), fvalidate=fvalidate) simple = Simple() with self.assertRaises(RuntimeError): simple.scalar_range = (5, 2) traits-4.6.0/traits/tests/test_weak_ref.py000066400000000000000000000040211300633736300206740ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Test cases for weakref (WeakRef) traits. """ import contextlib import gc from traits.testing.unittest_tools import unittest, UnittestTools from ..trait_types import Str, WeakRef from ..has_traits import HasTraits class Eggs(HasTraits): name = Str class Spam(HasTraits): eggs = WeakRef(Eggs) @contextlib.contextmanager def restore_gc_state(): """Ensure that gc state is restored on exit of the with statement.""" originally_enabled = gc.isenabled() try: yield finally: if originally_enabled: gc.enable() else: gc.disable() class TestWeakRef(UnittestTools, unittest.TestCase): """ Test cases for weakref (WeakRef) traits. """ def test_set_and_get(self): eggs = Eggs(name='platypus') spam = Spam() self.assertIsNone(spam.eggs) spam.eggs = eggs self.assertIs(spam.eggs, eggs) del eggs self.assertIsNone(spam.eggs) def test_target_freed_notification(self): eggs = Eggs(name='duck') spam = Spam(eggs=eggs) # Removal of the last reference to 'eggs' should trigger notification. with self.assertTraitChanges(spam, 'eggs'): del eggs def test_weakref_trait_doesnt_leak_cycles(self): eggs = Eggs(name='ostrich') with restore_gc_state(): gc.disable() gc.collect() spam = Spam(eggs=eggs) del spam self.assertEqual(gc.collect(), 0) traits-4.6.0/traits/tests/tuple_test_mixin.py000066400000000000000000000054471300633736300214630ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # # Copyright (c) 2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # ----------------------------------------------------------------------------- from traits.api import HasTraits, TraitError from traits.testing.unittest_tools import UnittestTools VALUES = ('value1', 33, None) class TupleTestMixin(UnittestTools): """ A mixin class for testing tuple like traits. TestCases should set the self.trait attribute during setUp for the tests to run. """ def test_default_values(self): # Check that the default values for t1 and t2 are correctly # derived from the VALUES tuple. dummy = self._create_class() self.assertEqual(dummy.t1, VALUES) self.assertEqual(dummy.t2, VALUES) def test_simple_assignment(self): # Check that we can assign different values of the correct type. dummy = self._create_class() with self.assertTraitChanges(dummy, 't1'): dummy.t1 = ('other value 1', 77, None) with self.assertTraitChanges(dummy, 't2'): dummy.t2 = ('other value 2', 99, None) def test_invalid_assignment_length(self): # Check that assigning a tuple of incorrect length # raises a TraitError. self._assign_invalid_values_length(('str', 44)) self._assign_invalid_values_length(('str', 33, None, [])) def test_type_checking(self): # Test that type checking is done for the 't1' attribute. dummy = self._create_class() other_tuple = ('other value', 75, True) with self.assertRaises(TraitError): dummy.t1 = other_tuple self.assertEqual(dummy.t1, VALUES) # Test that no type checking is done for the 't2' attribute. try: dummy.t2 = other_tuple except TraitError: self.fail('Unexpected TraitError when assigning to tuple.') self.assertEqual(dummy.t2, other_tuple) def _assign_invalid_values_length(self, values): dummy = self._create_class() with self.assertRaises(TraitError): dummy.t1 = values self.assertEqual(dummy.t1, VALUES) with self.assertRaises(TraitError): dummy.t2 = values self.assertEqual(dummy.t2, VALUES) def _create_class(self): trait = self.trait class Dummy(HasTraits): t1 = trait(VALUES) t2 = trait(*VALUES) return Dummy() traits-4.6.0/traits/trait_base.py000066400000000000000000000327571300633736300170460ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 06/21/2002 # # Refactored into a separate module: 07/04/2003 # #------------------------------------------------------------------------------ """ Defines common, low-level capabilities needed by the Traits package. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import os import sys from os import getcwd from os.path import dirname, exists, join from . import _py2to3 from .etsconfig.api import ETSConfig # backwards compatibility: trait_base used to provide a patched enumerate enumerate = enumerate # Set the Python version being used: vi = sys.version_info python_version = vi[0] + (float( vi[1] ) / 10.0) #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- ClassTypes = _py2to3.ClassTypes SequenceTypes = ( list, tuple ) ComplexTypes = ( float, int ) TypeTypes = ( str, unicode, int, long, float, complex, list, tuple, dict, bool ) TraitNotifier = '__trait_notifier__' # The standard Traits property cache prefix: TraitsCache = '_traits_cache_' #------------------------------------------------------------------------------- # Singleton 'Uninitialized' object: #------------------------------------------------------------------------------- Uninitialized = None class _Uninitialized(object): """ The singleton value of this class represents the uninitialized state of a trait and is specified as the 'old' value in the trait change notification that occurs when the value of a trait is read before being set. """ def __new__(cls): if Uninitialized is not None: return Uninitialized else: self = object.__new__(cls) return self def __repr__(self): return '' def __reduce_ex__(self, protocol): return (_Uninitialized, ()) #: When the first reference to a trait is a 'get' reference, the default value of #: the trait is implicitly assigned and returned as the value of the trait. #: Because of this implicit assignment, a trait change notification is #: generated with the Uninitialized object as the 'old' value of the trait, and #: the default trait value as the 'new' value. This allows other parts of the #: traits package to recognize the assignment as the implicit default value #: assignment, and treat it specially. Uninitialized = _Uninitialized() #------------------------------------------------------------------------------- # Singleton 'Undefined' object (used as undefined trait name and/or value): #------------------------------------------------------------------------------- Undefined = None class _Undefined(object): """ Singleton 'Undefined' object (used as undefined trait name and/or value) """ def __new__(cls): if Undefined is not None: return Undefined else: self = object.__new__(cls) return self def __repr__(self): return '' def __reduce_ex__(self, protocol): return (_Undefined, ()) def __eq__(self, other): return type(self) is type(other) def __hash__(self): return hash(type(self)) def __ne__(self, other): return type(self) is not type(other) #: Singleton object that indicates that a trait attribute has not yet had a #: value set (i.e., its value is undefined). This object is used instead of #: None, because None often has other meanings, such as that a value is not #: used. When a trait attribute is first assigned a value, and its associated #: trait notification handlers are called, Undefined is passed as the *old* #: parameter, to indicate that the attribute previously had no value. Undefined = _Undefined() # Tell the C-base code about singleton 'Undefined' and 'Uninitialized' objects: from . import ctraits ctraits._undefined( Undefined, Uninitialized ) #------------------------------------------------------------------------------- # Singleton 'Missing' object (used as missing method argument marker): #------------------------------------------------------------------------------- class Missing ( object ): """ Singleton 'Missing' object (used as missing method argument marker). """ def __repr__ ( self ): return '' #: Singleton object that indicates that a method argument is missing from a #: type-checked method signature. Missing = Missing() #------------------------------------------------------------------------------- # Singleton 'Self' object (used as object reference to current 'object'): #------------------------------------------------------------------------------- class Self ( object ): """ Singleton 'Self' object (used as object reference to current 'object'). """ def __repr__ ( self ): return '' #: Singleton object that references the current 'object'. Self = Self() #------------------------------------------------------------------------------- # Define a special 'string' coercion function: #------------------------------------------------------------------------------- def strx ( arg ): """ Wraps the built-in str() function to raise a TypeError if the argument is not of a type in StringTypes. """ if isinstance( arg, StringTypes ): return str( arg ) raise TypeError #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- StringTypes = ( str, unicode, int, long, float, complex ) #------------------------------------------------------------------------------- # Define a mapping of coercable types: #------------------------------------------------------------------------------- # Mapping of coercable types. CoercableTypes = { long: ( 11, long, int ), float: ( 11, float, int ), complex: ( 11, complex, float, int ), unicode: ( 11, unicode, str ) } #------------------------------------------------------------------------------- # Return a string containing the class name of an object with the correct # article (a or an) preceding it (e.g. 'an Image', 'a PlotValue'): #------------------------------------------------------------------------------- def class_of ( object ): """ Returns a string containing the class name of an object with the correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image', 'a PlotValue'). """ if isinstance( object, basestring ): return add_article( object ) return add_article( object.__class__.__name__ ) #------------------------------------------------------------------------------- # Return a string containing the right article (i.e. 'a' or 'an') prefixed to # a specified string: #------------------------------------------------------------------------------- def add_article ( name ): """ Returns a string containing the correct indefinite article ('a' or 'an') prefixed to the specified string. """ if name[:1].lower() in 'aeiou': return 'an ' + name return 'a ' + name #---------------------------------------------------------------------------- # Return a 'user-friendly' name for a specified trait: #---------------------------------------------------------------------------- def user_name_for ( name ): """ Returns a "user-friendly" version of a string, with the first letter capitalized and with underscore characters replaced by spaces. For example, ``user_name_for('user_name_for')`` returns ``'User name for'``. """ name = name.replace( '_', ' ' ) result = '' last_lower = False for c in name: if c.isupper() and last_lower: result += ' ' last_lower = c.islower() result += c return result.capitalize() #------------------------------------------------------------------------------- # Gets the path to the traits home directory: #------------------------------------------------------------------------------- _traits_home = None def traits_home ( ): """ Gets the path to the Traits home directory. """ global _traits_home if _traits_home is None: _traits_home = verify_path( join( ETSConfig.application_data, 'traits' ) ) return _traits_home #------------------------------------------------------------------------------- # Verify that a specified path exists, and try to create it if it doesn't: #------------------------------------------------------------------------------- def verify_path ( path ): """ Verify that a specified path exists, and try to create it if it does not exist. """ if not exists( path ): try: os.mkdir( path ) except: pass return path #------------------------------------------------------------------------------- # Returns the name of the module the caller's caller is located in: #------------------------------------------------------------------------------- def get_module_name ( level = 2 ): """ Returns the name of the module that the caller's caller is located in. """ return sys._getframe( level ).f_globals.get( '__name__', '__main__' ) #------------------------------------------------------------------------------- # Returns a resource path calculated from the caller's stack: #------------------------------------------------------------------------------- def get_resource_path ( level = 2 ): """Returns a resource path calculated from the caller's stack. """ module = sys._getframe( level ).f_globals.get( '__name__', '__main__' ) path = None if module != '__main__': # Return the path to the module: try: path = dirname( getattr( sys.modules.get( module ), '__file__' ) ) except: # Apparently 'module' is not a registered module...treat it like # '__main__': pass if path is None: # '__main__' is not a real module, so we need a work around: for path in [ dirname( sys.argv[0] ), getcwd() ]: if exists( path ): break # Handle application bundlers. Since the python source files may be placed # in a zip file and therefore won't be directly accessable using standard # open/read commands, the app bundlers will look for resources (i.e. data # files, images, etc.) in specific locations. For py2app, this is in the # [myapp].app/Contents/Resources directory. For py2exe, this is the same # directory as the [myapp].exe executable file generated by py2exe. frozen = getattr(sys, 'frozen', False) if frozen: if frozen == 'macosx_app': root = os.environ['RESOURCEPATH'] elif frozen in ('dll', 'windows_exe', 'console_exe'): root = os.path.dirname(sys.executable) else: # Unknown app bundler, but try anyway root = os.path.dirname(sys.executable) if ".zip/" in path: zippath, image_path = path.split(".zip/") path = os.path.join(root, image_path) return path #------------------------------------------------------------------------------- # Returns the value of an extended object attribute name of the form: # name[.name2[.name3...]]: #------------------------------------------------------------------------------- def xgetattr( object, xname, default = Undefined ): """ Returns the value of an extended object attribute name of the form: name[.name2[.name3...]]. """ names = xname.split( '.' ) for name in names[:-1]: if default is Undefined: object = getattr( object, name ) else: object = getattr( object, name, None ) if object is None: return default if default is Undefined: return getattr( object, names[-1] ) return getattr( object, names[-1], default ) #------------------------------------------------------------------------------- # Sets the value of an extended object attribute name of the form: # name[.name2[.name3...]]: #------------------------------------------------------------------------------- def xsetattr( object, xname, value ): """ Sets the value of an extended object attribute name of the form: name[.name2[.name3...]]. """ names = xname.split( '.' ) for name in names[:-1]: object = getattr( object, name ) setattr( object, names[-1], value ) #------------------------------------------------------------------------------- # Traits metadata selection functions: #------------------------------------------------------------------------------- def is_none ( value ): return (value is None) def not_none ( value ): return (value is not None) def not_false ( value ): return (value is not False) def not_event ( value ): return (value != 'event') def is_str ( value ): return isinstance( value, basestring ) traits-4.6.0/traits/trait_errors.py000066400000000000000000000105101300633736300174270ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 06/21/2002 # #------------------------------------------------------------------------------ """ Defines the standard exceptions raised by the Traits package. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import sys from .trait_base import class_of from . import _py2to3 #------------------------------------------------------------------------------- # Utilities #------------------------------------------------------------------------------- def repr_type(obj): """ Return a string representation of a value and its type for readable error messages. """ the_type = _py2to3.type_w_old_style(obj) msg = '%r %r' % (obj, the_type) return msg #------------------------------------------------------------------------------- # 'TraitError' class: #------------------------------------------------------------------------------- class TraitError ( Exception ): def __init__ ( self, args = None, name = None, info = None, value = None ): if name is None: # If the given args is not a tuple then assume that the user intended # it to be the single item in a one-element tuple. if not isinstance(args, tuple): args = args, self.args = args else: # Save the information, in case the 'args' object is not the correct # one, and we need to regenerate the message later: self.name = name self.info = info self.value = value self.desc = None self.prefix = 'The' self.set_desc( None, args ) def set_desc ( self, desc, object = None ): if hasattr( self, 'desc' ): if desc is not None: self.desc = desc if object is not None: self.object = object self.set_args() def set_prefix ( self, prefix ): if hasattr( self, 'prefix' ): self.prefix = prefix self.set_args() def set_args ( self ): if self.desc is None: extra = '' else: extra = ' specifies %s and' % self.desc obj = getattr( self, 'object', None ) # Note: self.args must be a tuple so be sure to leave the trailing # commas. the_type = _py2to3.type_w_old_style(self.value) if obj is not None: self.args = ("%s '%s' trait of %s instance%s must be %s, " "but a value of %s was specified." % ( self.prefix, self.name, class_of(obj), extra, self.info, repr_type(self.value))), else: self.args = ("%s '%s' trait%s must be %s, but a value of %s was " "specified." % (self.prefix, self.name, extra, self.info, repr_type(self.value))), #------------------------------------------------------------------------------- # 'TraitNotificationError' class: #------------------------------------------------------------------------------- class TraitNotificationError ( Exception ): pass #------------------------------------------------------------------------------- # 'DelegationError' class: #------------------------------------------------------------------------------- class DelegationError ( TraitError ): def __init__ ( self, args ): # .args must be a tuple. self.args = args, #------------------------------------------------------------------------------- # Export the defined exceptions to the C-base traits module: #------------------------------------------------------------------------------- from . import ctraits ctraits._exceptions( TraitError, DelegationError ) traits-4.6.0/traits/trait_handlers.py000066400000000000000000003533501300633736300177270ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 06/21/2002 # # Refactored into a separate module: 07/04/2003 # #------------------------------------------------------------------------------ """ Defines the BaseTraitHandler class and a standard set of BaseTraitHandler subclasses for use with the Traits package. A trait handler mediates the assignment of values to object traits. It verifies (via its validate() method) that a specified value is consistent with the object trait, and generates a TraitError exception if it is not consistent. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import sys import re import copy import copy_reg from types import FunctionType, MethodType TypeType = type from weakref import ref from .trait_base import (strx, SequenceTypes, Undefined, TypeTypes, ClassTypes, CoercableTypes, TraitsCache, class_of, Missing) from .trait_errors import TraitError, repr_type from . import _py2to3 # Patched by 'traits.py' once class is defined! Trait = Event = None # Set up a logger: import logging logger = logging.getLogger( __name__ ) #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- # Trait 'comparison_mode' enum values: NO_COMPARE = 0 OBJECT_IDENTITY_COMPARE = 1 RICH_COMPARE = 2 RangeTypes = ( int, long, float ) CallableTypes = ( FunctionType, MethodType ) # Mapping from trait metadata 'type' to CTrait 'type': trait_types = { 'python': 1, 'event': 2 } #------------------------------------------------------------------------------- # Forward references: #------------------------------------------------------------------------------- trait_from = None # Patched by 'traits.py' when real 'trait_from' is defined #------------------------------------------------------------------------------- # Returns the correct argument count for a specified function or method: #------------------------------------------------------------------------------- def _arg_count ( func ): """ Returns the correct argument count for a specified function or method. """ if (type( func ) is MethodType) and (func.im_self is not None): return func.func_code.co_argcount - 1 return func.func_code.co_argcount #------------------------------------------------------------------------------- # Property error handling functions: #------------------------------------------------------------------------------- def _write_only ( object, name ): raise TraitError, "The '%s' trait of %s instance is 'write only'." % ( name, class_of( object ) ) def _read_only ( object, name, value ): raise TraitError, "The '%s' trait of %s instance is 'read only'." % ( name, class_of( object ) ) def _undefined_get ( object, name ): raise TraitError, ("The '%s' trait of %s instance is a property that has " "no 'get' or 'set' method") % ( name, class_of( object ) ) def _undefined_set ( object, name, value ): _undefined_get( object, name ) #------------------------------------------------------------------------------- # 'BaseTraitHandler' class (base class for all user defined traits and trait # handlers): #------------------------------------------------------------------------------- class BaseTraitHandler ( object ): """ The task of this class and its subclasses is to verify the correctness of values assigned to object trait attributes. This class is an alternative to trait validator functions. A trait handler has several advantages over a trait validator function, due to being an object: * Trait handlers have constructors and state. Therefore, you can use them to create *parametrized types*. * Trait handlers can have multiple methods, whereas validator functions can have only one callable interface. This feature allows more flexibility in their implementation, and allows them to handle a wider range of cases, such as interactions with other components. """ default_value_type = -1 has_items = False is_mapped = False editor = None info_text = 'a legal value' def is_valid ( self, object, name, value ): try: validate = self.validate try: validate( object, name, value ) return True except: return False except: return True def error ( self, object, name, value ): """Raises a TraitError exception. Parameters ---------- object : object The object whose attribute is being assigned. name : str The name of the attribute being assigned. value : object The proposed new value for the attribute. Description ----------- This method is called by the validate() method when an assigned value is not valid. Raising a TraitError exception either notifies the user of the problem, or, in the case of compound traits, provides a chance for another trait handler to handle to validate the value. """ raise TraitError( object, name, self.full_info( object, name, value ), value ) def full_info ( self, object, name, value ): """Returns a string describing the type of value accepted by the trait handler. Parameters ---------- object : object The object whose attribute is being assigned. name : str The name of the attribute being assigned. value : The proposed new value for the attribute. Description ----------- The string should be a phrase describing the type defined by the TraitHandler subclass, rather than a complete sentence. For example, use the phrase, "a square sprocket" instead of the sentence, "The value must be a square sprocket." The value returned by full_info() is combined with other information whenever an error occurs and therefore makes more sense to the user if the result is a phrase. The full_info() method is similar in purpose and use to the **info** attribute of a validator function. Note that the result can include information specific to the particular trait handler instance. For example, TraitRange instances return a string indicating the range of values acceptable to the handler (e.g., "an integer in the range from 1 to 9"). If the full_info() method is not overridden, the default method returns the value of calling the info() method. """ return self.info() def info ( self ): """Must return a string describing the type of value accepted by the trait handler. The string should be a phrase describing the type defined by the TraitHandler subclass, rather than a complete sentence. For example, use the phrase, "a square sprocket" instead of the sentence, "The value must be a square sprocket." The value returned by info() is combined with other information whenever an error occurs and therefore makes more sense to the user if the result is a phrase. The info() method is similar in purpose and use to the **info** attribute of a validator function. Note that the result can include information specific to the particular trait handler instance. For example, TraitRange instances return a string indicating the range of values acceptable to the handler (e.g., "an integer in the range from 1 to 9"). If the info() method is not overridden, the default method returns the value of the 'info_text' attribute. """ return self.info_text def repr ( self, value ): """ Returns a printable representation of a value along with its type. .. deprecated :: 3.0.3 This functionality was only used to provide readable error messages. This functionality has been incorporated into TraitError itself. Parameters ---------- value : object The value to be printed. """ import warnings warnings.warn("this functionality has been merged into TraitError; " "just pass the raw value", DeprecationWarning) return repr_type(value) def get_editor ( self, trait = None ): """ Returns a trait editor that allows the user to modify the *trait* trait. Parameters ---------- trait : Trait The trait to be edited. Description ----------- This method only needs to be specified if traits defined using this trait handler require a non-default trait editor in trait user interfaces. The default implementation of this method returns a trait editor that allows the user to type an arbitrary string as the value. For more information on trait user interfaces, refer to the *Traits UI User Guide*. """ if self.editor is None: self.editor = self.create_editor() return self.editor def create_editor ( self ): """ Returns the default traits UI editor to use for a trait. """ from traitsui.api import TextEditor return TextEditor() def inner_traits ( self ): """ Returns a tuple containing the *inner traits* for this trait. Most trait handlers do not have any inner traits, and so will return an empty tuple. The exceptions are **List** and **Dict** trait types, which have inner traits used to validate the values assigned to the trait. For example, in *List( Int )*, the *inner traits* for **List** are ( **Int**, ). """ return () #------------------------------------------------------------------------------- # 'TraitType' (base class for class-based trait definitions: #------------------------------------------------------------------------------- # Create a singleton object for use in the TraitType constructor: class NoDefaultSpecified ( object ): pass NoDefaultSpecified = NoDefaultSpecified() class TraitType ( BaseTraitHandler ): """ Base class for new trait types. This class enables you to define new traits using a class-based approach, instead of by calling the Trait() factory function with an instance of a TraitHandler derived object. When subclassing this class, you can implement one or more of the method signatures below. Note that these methods are defined only as comments, because the absence of method definitions in the subclass definition implicitly provides information about how the trait should operate. The optional methods are as follows: * **get ( self, object, name ):** This is the getter method of a trait that behaves like a property. :Parameters: **object** (*object*) -- The object that the property applies to. **name** (str) -- The name of the property on *object* property. *Description* If neither this method nor the set() method is defined, the value of the trait is handled like a normal object attribute. If this method is not defined, but the set() method is defined, the trait behaves like a write-only property. This method should return the value of the *name* property for the *object* object. * **set ( self, object, name, value )** This is the setter method of a trait that behaves like a property. :Parameters: **object** (*object*) -- The object that the property applies to. **name** (str) -- The name of the property on *object*. **value** -- The value being assigned as the value of the property. *Description* If neither this method nor the get() method is implemented, the trait behaves like a normal trait attribute. If this method is not defined, but the get() method is defined, the trait behaves like a read-only property. This method does not need to return a value, but it should raise a TraitError exception if the specified *value* is not valid and cannot be coerced or adapted to a valid value. * **validate ( self, object, name, value )** This method validates, coerces, or adapts the specified *value* as the value of the *name* trait of the *object* object. This method is called when a value is assigned to an object trait that is based on this subclass of *TraitType* and the class does not contain a definition for either the get() or set() methods. This method must return the original *value* or any suitably coerced or adapted value that is a legal value for the trait. If *value* is not a legal value for the trait, and cannot be coerced or adapted to a legal value, the method should either raise a **TraitError** or call the **error** method to raise the **TraitError** on its behalf. * **is_valid_for ( self, value )** As an alternative to implementing the **validate** method, you can instead implement the **is_valid_for** method, which receives only the *value* being assigned. It should return **True** if the value is valid, and **False** otherwise. * **value_for ( self, value )** As another alternative to implementing the **validate** method, you can instead implement the **value_for** method, which receives only the *value* being assigned. It should return the validated form of *value* if it is valid, or raise a **TraitError** if the value is not valid. * **post_setattr ( self, object, name, value )** This method allows the trait to do additional processing after *value* has been successfully assigned to the *name* trait of the *object* object. For most traits there is no additional processing that needs to be done, and this method need not be defined. It is normally used for creating "shadow" (i.e., "mapped" traits), but other uses may arise as well. This method does not need to return a value, and should normally not raise any exceptions. """ default_value = Undefined metadata = {} def __init__ ( self, default_value = NoDefaultSpecified, **metadata ): """ This constructor method is the only method normally called directly by client code. It defines the trait. The default implementation accepts an optional, untype-checked default value, and caller-supplied trait metadata. Override this method whenever a different method signature or a type-checked default value is needed. """ if default_value is not NoDefaultSpecified: self.default_value = default_value if len( metadata ) > 0: if len( self.metadata ) > 0: self._metadata = self.metadata.copy() self._metadata.update( metadata ) else: self._metadata = metadata # By default, private traits are not visible. if (self._metadata.get('private') and self._metadata.get('visible') is None): self._metadata['visible'] = False else: self._metadata = self.metadata.copy() self.init() def init ( self ): """ Allows the trait to perform any additional initialization needed. """ pass def get_default_value ( self ): """ Returns a tuple of the form: (*default_value_type*, *default_value*) which describes the default value for this trait. The default implementation analyzes the value of the trait's **default_value** attribute and determines an appropriate *default_value_type* for *default_value*. If you need to override this method to provide a different result tuple, the following values are valid values for *default_value_type*: - 0, 1: The *default_value* item of the tuple is the default value. - 2: The object containing the trait is the default value. - 3: A new copy of the list specified by *default_value* is the default value. - 4: A new copy of the dictionary specified by *default_value* is the default value. - 5: A new instance of TraitListObject constructed using the *default_value* list is the default value. - 6: A new instance of TraitDictObject constructed using the *default_value* dictionary is the default value. - 7: *default_value* is a tuple of the form: (*callable*, *args*, *kw*), where *callable* is a callable, *args* is a tuple, and *kw* is either a dictionary or None. The default value is the result obtained by invoking callable(\*args, \*\*kw). - 8: *default_value* is a callable. The default value is the result obtained by invoking *default_value*(*object*), where *object* is the object containing the trait. If the trait has a validate() method, the validate() method is also called to validate the result. - 9: A new instance of TraitSetObject constructed using the *default_value* set is the default value. """ dv = self.default_value dvt = self.default_value_type if dvt < 0: dvt = 0 if isinstance( dv, TraitListObject ): dvt = 5 elif isinstance( dv, list ): dvt = 3 elif isinstance( dv, TraitDictObject ): dvt = 6 elif isinstance( dv, dict ): dvt = 4 elif isinstance( dv, TraitSetObject ): dvt = 9 self.default_value_type = dvt return ( dvt, dv ) def clone ( self, default_value = Missing, **metadata ): """ Clones the contents of this object into a new instance of the same class, and then modifies the cloned copy using the specified *default_value* and *metadata*. Returns the cloned object as the result. Note that subclasses can change the signature of this method if needed, but should always call the 'super' method if possible. """ if 'parent' not in metadata: metadata[ 'parent' ] = self new = self.__class__.__new__( self.__class__ ) new_dict = new.__dict__ new_dict.update( self.__dict__ ) if 'editor' in new_dict: del new_dict[ 'editor' ] if '_metadata' in new_dict: new._metadata = new._metadata.copy() else: new._metadata = {} new._metadata.update( metadata ) if default_value is not Missing: new.default_value = default_value if self.validate is not None: try: new.default_value = self.validate( None, None, default_value ) except: pass return new def get_value ( self, object, name, trait = None ): """ Returns the current value of a property-based trait. """ cname = TraitsCache + name value = object.__dict__.get( cname, Undefined ) if value is Undefined: if trait is None: trait = object.trait( name ) object.__dict__[ cname ] = value = \ trait.default_value_for( object, name ) return value def set_value ( self, object, name, value ): """ Sets the cached value of a property-based trait and fires the appropriate trait change event. """ cname = TraitsCache + name old = object.__dict__.get( cname, Undefined ) if value != old: object.__dict__[ cname ] = value object.trait_property_changed( name, old, value ) #-- Private Methods -------------------------------------------------------- def __call__ ( self, *args, **kw ): """ Allows a derivative trait to be defined from this one. """ return self.clone( *args, **kw ).as_ctrait() def _is_valid_for ( self, object, name, value ): """ Handles a simplified validator that only returns whether or not the original value is valid. """ if self.is_valid_for( value ): return value self.error( object, name, value ) def _value_for ( self, object, name, value ): """ Handles a simplified validator that only receives the value argument. """ try: return self.value_for( value ) except TraitError: self.error( object, name, value ) def as_ctrait ( self ): """ Returns a CTrait corresponding to the trait defined by this class. """ from .traits import CTrait metadata = getattr( self, '_metadata', {} ) getter = getattr( self, 'get', None ) setter = getattr( self, 'set', None ) if (getter is not None) or (setter is not None): if getter is None: getter = _write_only metadata.setdefault( 'transient', True ) elif setter is None: setter = _read_only metadata.setdefault( 'transient', True ) trait = CTrait( 4 ) n = 0 validate = getattr( self, 'validate', None ) if validate is not None: n = _arg_count( validate ) trait.property( getter, _arg_count( getter ), setter, _arg_count( setter ), validate, n ) metadata.setdefault( 'type', 'property' ) else: type = getattr( self, 'ctrait_type', None ) if type is None: type = trait_types.get( metadata.get( 'type' ), 0 ) trait = CTrait( type ) validate = getattr( self, 'fast_validate', None ) if validate is None: validate = getattr( self, 'validate', None ) if validate is None: validate = getattr( self, 'is_valid_for', None ) if validate is not None: validate = self._is_valid_for else: validate = getattr( self, 'value_for', None ) if validate is not None: validate = self._value_for if validate is not None: trait.set_validate( validate ) post_setattr = getattr( self, 'post_setattr', None ) if post_setattr is not None: trait.post_setattr = post_setattr trait.is_mapped( self.is_mapped ) # Note: The use of 'rich_compare' metadata is deprecated; use # 'comparison_mode' metadata instead: rich_compare = metadata.get( 'rich_compare' ) if rich_compare is not None: trait.rich_comparison( rich_compare is True ) comparison_mode = metadata.get( 'comparison_mode' ) if comparison_mode is not None: trait.comparison_mode( comparison_mode ) metadata.setdefault( 'type', 'trait' ) trait.default_value( *self.get_default_value() ) trait.value_allowed( metadata.get( 'trait_value', False ) is True ) trait.handler = self trait.__dict__ = metadata.copy() return trait def __getattr__ ( self, name ): if (name[:2] == '__') and (name[-2:] == '__'): raise AttributeError( "'%s' object has no attribute '%s'" % ( self.__class__.__name__, name ) ) return getattr( self, '_metadata', {} ).get( name, None ) #------------------------------------------------------------------------------- # 'TraitHandler' class (base class for all trait handlers): #------------------------------------------------------------------------------- class TraitHandler ( BaseTraitHandler ): """ The task of this class and its subclasses is to verify the correctness of values assigned to object trait attributes. This class is an alternative to trait validator functions. A trait handler has several advantages over a trait validator function, due to being an object: * Trait handlers have constructors and state. Therefore, you can use them to create *parametrized types*. * Trait handlers can have multiple methods, whereas validator functions can have only one callable interface. This feature allows more flexibility in their implementation, and allows them to handle a wider range of cases, such as interactions with other components. The only method of TraitHandler that *must* be implemented by subclasses is validate(). """ def validate ( self, object, name, value ): """ Verifies whether a new value assigned to a trait attribute is valid. Parameters ---------- object : object The object whose attribute is being assigned. name : str The name of the attribute being assigned. value : The proposed new value for the attribute. Returns ------- If the new value is valid, this method must return either the original value passed to it, or an alternate value to be assigned in place of the original value. Whatever value this method returns is the actual value assigned to *object.name*. Description ----------- This method *must* be implemented by subclasses of TraitHandler. It is called whenever a new value is assigned to a trait attribute defined using this trait handler. If the value received by validate() is not valid for the trait attribute, the method must called the predefined error() method to raise a TraitError exception """ raise TraitError, ( "The '%s' trait of %s instance has an unknown type. " "Contact the developer to correct the problem." % ( name, class_of( object ) ) ) #------------------------------------------------------------------------------- # 'TraitRange' class: #------------------------------------------------------------------------------- class TraitRange ( TraitHandler ): """Ensures that a trait attribute lies within a specified numeric range. TraitRange is the underlying handler for the predefined Range() trait factory. Any value assigned to a trait containing a TraitRange handler must be of the correct type and in the numeric range defined by the TraitRange instance. No automatic coercion takes place. For example:: class Person(HasTraits): age = Trait(0, TraitRange(0, 150)) weight = Trait(0.0, TraitRange(0.0, None)) This example defines a Person class, which has an **age** trait attribute, which must be an integer/long in the range from 0 to 150, and a **weight** trait attribute, which must be a non-negative float value. """ def __init__ ( self, low = None, high = None, exclude_low = False, exclude_high = False ): """ Creates a TraitRange handler. Parameters ---------- low : number The minimum value that the trait can accept. high : number The maximum value that the trait can accept. exclude_low : bool Should the *low* value be exclusive (or inclusive). exclude_high : bool Should the *high* value be exclusive (or inclusive). Description ----------- The *low* and *high* values must be of the same Python numeric type, either ``int``, ``long`` or ``float``. Alternatively, one of the values may be None, to indicate that that portion of the range is unbounded. The *exclude_low* and *exclude_high* values can be used to specify whether the *low* and *high* values should be exclusive (or inclusive). """ vtype = type( high ) if (low is not None) and (vtype is not float): vtype = type( low ) if vtype not in RangeTypes: raise TraitError, ("TraitRange can only be use for int, long or " "float values, but a value of type %s was " "specified." % vtype) if vtype is float: self.validate = self.float_validate kind = 4 self._type_desc = 'a floating point number' if low is not None: low = float( low ) if high is not None: high = float( high ) elif vtype is long: self.validate = self.long_validate self._type_desc = 'a long integer' if low is not None: low = long( low ) if high is not None: high = long( high ) else: self.validate = self.int_validate kind = 3 self._type_desc = 'an integer' if low is not None: low = int( low ) if high is not None: high = int( high ) exclude_mask = 0 if exclude_low: exclude_mask |= 1 if exclude_high: exclude_mask |= 2 if vtype is not long: self.fast_validate = ( kind, low, high, exclude_mask ) # Assign type-corrected arguments to handler attributes self._low = low self._high = high self._exclude_low = exclude_low self._exclude_high = exclude_high def float_validate ( self, object, name, value ): try: if (isinstance( value, RangeTypes ) and ((self._low is None) or (self._exclude_low and (self._low < value)) or ((not self._exclude_low) and (self._low <= value))) and ((self._high is None) or (self._exclude_high and (self._high > value)) or ((not self._exclude_high) and (self._high >= value)))): return float( value ) except: pass self.error( object, name, value ) def int_validate ( self, object, name, value ): try: if (isinstance( value, int ) and ((self._low is None) or (self._exclude_low and (self._low < value)) or ((not self._exclude_low) and (self._low <= value))) and ((self._high is None) or (self._exclude_high and (self._high > value)) or ((not self._exclude_high) and (self._high >= value)))): return value except: pass self.error( object, name, value ) def long_validate ( self, object, name, value ): try: if (isinstance( value, long ) and ((self._low is None) or (self._exclude_low and (self._low < value)) or ((not self._exclude_low) and (self._low <= value))) and ((self._high is None) or (self._exclude_high and (self._high > value)) or ((not self._exclude_high) and (self._high >= value)))): return value except: pass self.error( object, name, value ) def info ( self ): if self._low is None: if self._high is None: return self._type_desc return '%s <%s %s' % ( self._type_desc, '='[ self._exclude_high: ], self._high ) elif self._high is None: return '%s >%s %s' % ( self._type_desc, '='[ self._exclude_low: ], self._low ) return '%s <%s %s <%s %s' % ( self._low, '='[ self._exclude_low: ], self._type_desc, '='[ self._exclude_high: ], self._high ) def get_editor ( self, trait ): from traitsui.api import RangeEditor auto_set = trait.auto_set if auto_set is None: auto_set = True return RangeEditor( self, mode = trait.mode or 'auto', cols = trait.cols or 3, auto_set = auto_set, enter_set = trait.enter_set or False, low_label = trait.low or '', high_label = trait.high or '' ) #------------------------------------------------------------------------------- # 'TraitString' class: #------------------------------------------------------------------------------- class TraitString ( TraitHandler ): """ Ensures that a trait attribute value is a string that satisfied some additional, optional constraints. The optional constraints include minimum and maximum lengths, and a regular expression that the string must match. If the value assigned to the trait attribute is a Python numeric type, the TraitString handler first coerces the value to a string. Values of other non-string types result in a TraitError being raised. The handler then makes sure that the resulting string is within the specified length range and that it matches the regular expression. Example ------- class Person(HasTraits): name = Trait('', TraitString(maxlen=50, regex=r'^[A-Za-z]*$')) This example defines a **Person** class with a **name** attribute, which must be a string of between 0 and 50 characters that consist of only upper and lower case letters. """ def __init__ ( self, minlen = 0, maxlen = sys.maxint, regex = '' ): """ Creates a TraitString handler. Parameters ---------- minlen : int The minimum length allowed for the string. maxlen : int The maximum length allowed for the string. regex : str A Python regular expression that the string must match. """ self.minlen = max( 0, minlen ) self.maxlen = max( self.minlen, maxlen ) self.regex = regex self._init() def _init ( self ): if self.regex != '': self.match = re.compile( self.regex ).match if (self.minlen == 0) and (self.maxlen == sys.maxint): self.validate = self.validate_regex elif (self.minlen == 0) and (self.maxlen == sys.maxint): self.validate = self.validate_str else: self.validate = self.validate_len def validate ( self, object, name, value ): try: value = strx( value ) if ((self.minlen <= len( value ) <= self.maxlen) and (self.match( value ) is not None)): return value except: pass self.error( object, name, value ) def validate_str ( self, object, name, value ): try: return strx( value ) except: pass self.error( object, name, value ) def validate_len ( self, object, name, value ): try: value = strx( value ) if self.minlen <= len( value ) <= self.maxlen: return value except: pass self.error( object, name, value ) def validate_regex ( self, object, name, value ): try: value = strx( value ) if self.match( value ) is not None: return value except: pass self.error( object, name, value ) def info ( self ): msg = '' if (self.minlen != 0) and (self.maxlen != sys.maxint): msg = ' between %d and %d characters long' % ( self.minlen, self.maxlen ) elif self.maxlen != sys.maxint: msg = ' <= %d characters long' % self.maxlen elif self.minlen != 0: msg = ' >= %d characters long' % self.minlen if self.regex != '': if msg != '': msg += ' and' msg += (" matching the pattern '%s'" % self.regex) return 'a string' + msg def __getstate__ ( self ): result = self.__dict__.copy() for name in [ 'validate', 'match' ]: if name in result: del result[ name ] return result def __setstate__ ( self, state ): self.__dict__.update( state ) self._init() #------------------------------------------------------------------------------- # 'TraitCoerceType' class: #------------------------------------------------------------------------------- class TraitCoerceType ( TraitHandler ): """Ensures that a value assigned to a trait attribute is of a specified Python type, or can be coerced to the specified type. TraitCoerceType is the underlying handler for the predefined traits and factories for Python simple types. The TraitCoerceType class is also an example of a parametrized type, because the single TraitCoerceType class allows creating instances that check for totally different sets of values. For example:: class Person(HasTraits): name = Trait('', TraitCoerceType('')) weight = Trait(0.0, TraitCoerceType(float)) In this example, the **name** attribute must be of type ``str`` (string), while the **weight** attribute must be of type ``float``, although both are based on instances of the TraitCoerceType class. Note that this example is essentially the same as writing:: class Person(HasTraits): name = Trait('') weight = Trait(0.0) This simpler form is automatically changed by the Trait() function into the first form, based on TraitCoerceType instances, when the trait attributes are defined. For attributes based on TraitCoerceType instances, if a value that is assigned is not of the type defined for the trait, a TraitError exception is raised. However, in certain cases, if the value can be coerced to the required type, then the coerced value is assigned to the attribute. Only *widening* coercions are allowed, to avoid any possible loss of precision. The following table lists the allowed coercions. ============ ================= Trait Type Coercible Types ============ ================= complex float, int float int long int unicode str ============ ================= """ def __init__ ( self, aType ): """ Creates a TraitCoerceType handler. Parameters ---------- aType : type Either a Python type (e.g., ``str`` or types.StringType) or a Python value (e.g., 'cat'). Description ----------- If *aType* is a value, it is mapped to its corresponding type. For example, the string 'cat' is automatically mapped to ``str`` (i.e., types.StringType). """ if not isinstance( aType, TypeType ): aType = type( aType ) self.aType = aType try: self.fast_validate = CoercableTypes[ aType ] except: self.fast_validate = ( 11, aType ) def validate ( self, object, name, value ): fv = self.fast_validate tv = type( value ) # If the value is already the desired type, then return it: if tv is fv[1]: return value # Else see if it is one of the coercable types: for typei in fv[2:]: if tv is typei: # Return the coerced value: return fv[1]( value ) # Otherwise, raise an exception: self.error( object, name, value ) def info ( self ): return 'a value of %s' % str( self.aType )[1:-1] def get_editor ( self, trait ): # Make the special case of a 'bool' type use the boolean editor: if self.aType is bool: if self.editor is None: from traitsui.api import BooleanEditor self.editor = BooleanEditor() return self.editor # Otherwise, map all other types to a text editor: auto_set = trait.auto_set if auto_set is None: auto_set = True from traitsui.api import TextEditor return TextEditor( auto_set = auto_set, enter_set = trait.enter_set or False, evaluate = self.fast_validate[1] ) #------------------------------------------------------------------------------- # 'TraitCastType' class: #------------------------------------------------------------------------------- class TraitCastType ( TraitCoerceType ): """Ensures that a value assigned to a trait attribute is of a specified Python type, or can be cast to the specified type. This class is similar to TraitCoerceType, but uses casting rather than coercion. Values are cast by calling the type with the value to be assigned as an argument. When casting is performed, the result of the cast is the value assigned to the trait attribute. Any trait that uses a TraitCastType instance in its definition ensures that its value is of the type associated with the TraitCastType instance. For example:: class Person(HasTraits): name = Trait('', TraitCastType('')) weight = Trait(0.0, TraitCastType(float)) In this example, the **name** trait must be of type ``str`` (string), while the **weight** trait must be of type ``float``. Note that this example is essentially the same as writing:: class Person(HasTraits): name = CStr weight = CFloat To understand the difference between TraitCoerceType and TraitCastType (and also between Float and CFloat), consider the following example:: >>>class Person(HasTraits): ... weight = Float ... cweight = CFloat >>> >>>bill = Person() >>>bill.weight = 180 # OK, coerced to 180.0 >>>bill.cweight = 180 # OK, cast to 180.0 >>>bill.weight = '180' # Error, invalid coercion >>>bill.cweight = '180' # OK, cast to float('180') """ def __init__ ( self, aType ): """ Creates a TraitCastType handler. Parameters ---------- aType : type Either a Python type (e.g., ``str`` or types.StringType) or a Python value (e.g., ``'cat``). Description ----------- If *aType* is a Python value, it is automatically mapped to its corresponding Python type. For example, the string 'cat' is automatically mapped to ``str`` (i.e., types.StringType). """ if not isinstance( aType, TypeType ): aType = type( aType ) self.aType = aType self.fast_validate = ( 12, aType ) def validate ( self, object, name, value ): # If the value is already the desired type, then return it: if type( value ) is self.aType: return value # Else try to cast it to the specified type: try: return self.aType( value ) except: self.error( object, name, value ) #------------------------------------------------------------------------------- # 'ThisClass' class: #------------------------------------------------------------------------------- class ThisClass ( TraitHandler ): """Ensures that the trait attribute values belong to the same class (or a subclass) as the object containing the trait attribute. ThisClass is the underlying handler for the predefined traits **This** and **self**, and the elements of ListThis. """ def __init__ ( self, allow_none = False ): """Creates a ThisClass handler. Parameters ---------- allow_none : bool Flag indicating whether None is accepted as a valid value (True or non-zero) or not (False or 0). """ if allow_none: self.validate = self.validate_none self.info = self.info_none self.fast_validate = ( 2, None ) else: self.fast_validate = ( 2, ) def validate ( self, object, name, value ): if isinstance( value, object.__class__ ): return value self.validate_failed( object, name, value ) def validate_none ( self, object, name, value ): if isinstance( value, object.__class__ ) or (value is None): return value self.validate_failed( object, name, value ) def info ( self ): return 'an instance of the same type as the receiver' def info_none ( self ): return 'an instance of the same type as the receiver or None' def validate_failed ( self, object, name, value ): self.error( object, name, value ) def get_editor ( self, trait ): if self.editor is None: from traitsui.api import InstanceEditor self.editor = InstanceEditor( label = trait.label or '', view = trait.view or '', kind = trait.kind or 'live' ) return self.editor #------------------------------------------------------------------------------- # 'TraitInstance' class: #------------------------------------------------------------------------------- # Mapping from 'adapt' parameter values to 'fast validate' values AdaptMap = { 'no': -1, 'yes': 0, 'default': 1 } class TraitInstance ( ThisClass ): """Ensures that trait attribute values belong to a specified Python class or type. TraitInstance is the underlying handler for the predefined trait **Instance** and the elements of List( Instance ). Any trait that uses a TraitInstance handler ensures that its values belong to the specified type or class (or one of its subclasses). For example:: class Employee(HasTraits): manager = Trait(None, TraitInstance(Employee, True)) This example defines a class Employee, which has a **manager** trait attribute, which accepts either None or an instance of Employee as its value. TraitInstance ensures that assigned values are exactly of the type specified (i.e., no coercion is performed). """ def __init__ ( self, aClass, allow_none = True, adapt = 'no', module = '' ): """Creates a TraitInstance handler. Parameters ---------- aClass : class or type A Python class, an instance of a Python class, or a Python type. allow_none : bool Flag indicating whether None is accepted as a valid value. (True or non-zero) or not (False or 0) adapt : str Value indicating how adaptation should be handled: - 'no' (-1): Adaptation is not allowed. - 'yes' (0): Adaptation is allowed and should raise an exception if adaptation fails. - 'default' (1): Adaption is allowed and should return the default value if adaptation fails. module : module The module that the class belongs to. Description ----------- If *aClass* is an instance, it is mapped to the class it is an instance of. """ self._allow_none = allow_none self.adapt = AdaptMap[ adapt ] self.module = module if isinstance( aClass, basestring ): self.aClass = aClass else: if not isinstance( aClass, ClassTypes ): aClass = aClass.__class__ self.aClass = aClass self.set_fast_validate() def allow_none ( self ): self._allow_none = True if hasattr( self, 'fast_validate' ): self.set_fast_validate() def set_fast_validate ( self ): if self.adapt < 0: fast_validate = [ 1, self.aClass ] if self._allow_none: fast_validate = [ 1, None, self.aClass ] if self.aClass in TypeTypes: fast_validate[0] = 0 self.fast_validate = tuple( fast_validate ) else: self.fast_validate = ( 19, self.aClass, self.adapt, self._allow_none ) def validate ( self, object, name, value ): from traits.adaptation.api import adapt if value is None: if self._allow_none: return value else: self.validate_failed( object, name, value ) if isinstance( self.aClass, basestring ): self.resolve_class( object, name, value ) if self.adapt < 0: if isinstance( value, self.aClass ): return value elif self.adapt == 0: try: return adapt( value, self.aClass ) except: pass else: # fixme: The 'None' value is not really correct. It should return # the default value for the trait, but the handler does not have # any way to know this currently. Since the 'fast validate' code # does the correct thing, this should not normally be a problem. return adapt( value, self.aClass, None ) self.validate_failed( object, name, value ) def info ( self ): aClass = self.aClass if type( aClass ) is not str: aClass = aClass.__name__ if self.adapt < 0: result = class_of( aClass ) else: result = ('an implementor of, or can be adapted to implement, %s' % aClass) if self._allow_none: return result + ' or None' return result def resolve_class ( self, object, name, value ): aClass = self.validate_class( self.find_class( self.aClass ) ) if aClass is None: self.validate_failed( object, name, value ) self.aClass = aClass # fixme: The following is quite ugly, because it wants to try and fix # the trait referencing this handler to use the 'fast path' now that the # actual class has been resolved. The problem is finding the trait, # especially in the case of List(Instance('foo')), where the # object.base_trait(...) value is the List trait, not the Instance # trait, so we need to check for this and pull out the List # 'item_trait'. Obviously this does not extend well to other traits # containing nested trait references (Dict?)... self.set_fast_validate() trait = object.base_trait( name ) handler = trait.handler if (handler is not self) and hasattr( handler, 'item_trait' ): trait = handler.item_trait trait.set_validate( self.fast_validate ) def find_class ( self, aClass ): module = self.module col = aClass.rfind( '.' ) if col >= 0: module = aClass[ : col ] aClass = aClass[ col + 1: ] theClass = getattr( sys.modules.get( module ), aClass, None ) if (theClass is None) and (col >= 0): try: mod = __import__( module , globals=globals(), level=1) for component in module.split( '.' )[1:]: mod = getattr( mod, component ) theClass = getattr( mod, aClass, None ) except: pass return theClass def validate_class ( self, aClass ): return aClass def create_default_value ( self, *args, **kw ): aClass = args[0] if isinstance( aClass, basestring ): aClass = self.validate_class( self.find_class( aClass ) ) if aClass is None: raise TraitError, 'Unable to locate class: ' + args[0] return aClass( *args[1:], **kw ) #------------------------------------------------------------------------------- # 'TraitWeakRef' class: #------------------------------------------------------------------------------- class TraitWeakRef ( TraitInstance ): def _get ( self, object, name ): value = getattr( object, name + '_', None ) if value is not None: return value.value() return None def _set ( self, object, name, value ): if value is not None: value = HandleWeakRef( object, name, value ) object.__dict__[ name + '_' ] = value def resolve_class ( self, object, name, value ): # fixme: We have to override this method to prevent the 'fast validate' # from being set up, since the trait using this is a 'property' style # trait which is not currently compatible with the 'fast_validate' # style (causes internal Python SystemError messages). aClass = self.find_class( self.aClass ) if aClass is None: self.validate_failed( object, name, value ) self.aClass = aClass #-- Private Class -------------------------------------------------------------- def _make_value_freed_callback ( object_ref, name ): def _value_freed ( value_ref ): object = object_ref() if object is not None: object.trait_property_changed( name, Undefined, None ) return _value_freed class HandleWeakRef ( object ): def __init__ ( self, object, name, value ): object_ref = ref( object ) _value_freed = _make_value_freed_callback( object_ref, name ) self.object = object_ref self.name = name self.value = ref( value, _value_freed ) #------------------------------------------------------------------------------- # 'TraitClass' class: #------------------------------------------------------------------------------- class TraitClass ( TraitHandler ): """Ensures that trait attribute values are subclasses of a specified class (or the class itself). A value is valid if it is a subclass of the specified class (including the class itself), or it is a string that is equivalent to the name of a valid class. """ def __init__ ( self, aClass ): """Creates a TraitClass handler. Parameters ---------- aClass : class A Python class. Description ----------- If *aClass* is an instance, it is mapped to the class it is an instance of. """ if _py2to3.is_old_style_instance(aClass): aClass = aClass.__class__ self.aClass = aClass def validate ( self, object, name, value ): try: if isinstance( value, basestring ): value = value.strip() col = value.rfind( '.' ) if col >= 0: module_name = value[:col] class_name = value[col + 1:] module = sys.modules.get( module_name ) if module is None: exec( 'import ' + module_name ) module = sys.modules[ module_name ] value = getattr( module, class_name ) else: value = globals().get( value ) if issubclass( value, self.aClass ): return value except: pass self.error( object, name, value ) def info ( self ): return 'a subclass of ' + self.aClass.__name__ #------------------------------------------------------------------------------- # 'TraitFunction' class: #------------------------------------------------------------------------------- class TraitFunction ( TraitHandler ): """Ensures that assigned trait attribute values are acceptable to a specified validator function. TraitFunction is the underlying handler for the predefined trait **Function**, and for the use of function references as arguments to the Trait() function. """ def __init__ ( self, aFunc ): """ Creates a TraitFunction handler. Parameters ---------- aFunc : function A function to validate trait attribute values. Description ----------- The signature of the function passed as an argument must be of the form *function* ( *object*, *name*, *value* ). The function must verify that *value* is a legal value for the *name* trait attribute of *object*. If it is, the value returned by the function is the actual value assigned to the trait attribute. If it is not, the function must raise a TraitError exception. """ if not isinstance( aFunc, CallableTypes ): raise TraitError, "Argument must be callable." self.aFunc = aFunc self.fast_validate = ( 13, aFunc ) def validate ( self, object, name, value ): try: return self.aFunc( object, name, value ) except TraitError: self.error( object, name, value ) def info ( self ): try: return self.aFunc.info except: if self.aFunc.__doc__: return self.aFunc.__doc__ return 'a legal value' #------------------------------------------------------------------------------- # 'TraitEnum' class: #------------------------------------------------------------------------------- class TraitEnum ( TraitHandler ): """ Ensures that a value assigned to a trait attribute is a member of a specified list of values. TraitEnum is the underlying handler for the forms of the Trait() function that take a list of possible values """ def __init__ ( self, *values ): """ Creates a TraitEnum handler. Parameters ---------- values : list or tuple Enumeration of all legal values for a trait. Description ----------- The list of legal values can be provided as a list of values. That is, ``TraitEnum([1, 2, 3])`` and ``TraitEnum(1, 2, 3)`` are equivalent. For example:: class Flower(HasTraits): color = Trait('white', TraitEnum(['white', 'yellow', 'red'])) kind = Trait('annual', TraitEnum('annual', 'perennial')) This example defines a Flower class, which has a **color** trait attribute, which can have as its value, one of the three strings, 'white', 'yellow', or 'red', and a **kind** trait attribute, which can have as its value, either of the strings 'annual' or 'perennial'. This is equivalent to the following class definition:: class Flower(HasTraits): color = Trait(['white', 'yellow', 'red']) kind = Trait('annual', 'perennial') The Trait() function automatically maps traits of the form shown in this example to the form shown in the preceding example whenever it encounters them in a trait definition. """ if (len( values ) == 1) and (type( values[0] ) in SequenceTypes): values = values[0] self.values = tuple( values ) self.fast_validate = ( 5, self.values ) def validate ( self, object, name, value ): if value in self.values: return value self.error( object, name, value ) def info ( self ): return ' or '.join( [ repr( x ) for x in self.values ] ) def get_editor ( self, trait ): from traitsui.api import EnumEditor return EnumEditor( values = self, cols = trait.cols or 3, evaluate = trait.evaluate, mode = trait.mode or 'radio' ) #------------------------------------------------------------------------------- # 'TraitPrefixList' class: #------------------------------------------------------------------------------- class TraitPrefixList ( TraitHandler ): """Ensures that a value assigned to a trait attribute is a member of a list of specified string values, or is a unique prefix of one of those values. TraitPrefixList is a variation on TraitEnum. The values that can be assigned to a trait attribute defined using a TraitPrefixList handler is the set of all strings supplied to the TraitPrefixList constructor, as well as any unique prefix of those strings. That is, if the set of strings supplied to the constructor is described by [*s*\ :sub:`1`\ , *s*\ :sub:`2`\ , ..., *s*\ :sub:`n`\ ], then the string *v* is a valid value for the trait if *v* == *s*\ :sub:`i[:j]` for one and only one pair of values (i, j). If *v* is a valid value, then the actual value assigned to the trait attribute is the corresponding *s*\ :sub:`i` value that *v* matched. Example ------- class Person(HasTraits): married = Trait('no', TraitPrefixList('yes', 'no') The Person class has a **married** trait that accepts any of the strings 'y', 'ye', 'yes', 'n', or 'no' as valid values. However, the actual values assigned as the value of the trait attribute are limited to either 'yes' or 'no'. That is, if the value 'y' is assigned to the **married** attribute, the actual value assigned will be 'yes'. Note that the algorithm used by TraitPrefixList in determining whether a string is a valid value is fairly efficient in terms of both time and space, and is not based on a brute force set of comparisons. """ def __init__ ( self, *values ): """ Creates a TraitPrefixList handler. Parameters ---------- values : list or tuple of strings Enumeration of all legal values for a trait. Description ----------- As with TraitEnum, the list of legal values can be provided as a list of values. That is, ``TraitPrefixList(['one', 'two', 'three'])`` and ``TraitPrefixList('one', 'two', 'three')`` are equivalent. """ if (len( values ) == 1) and (type( values[0] ) in SequenceTypes): values = values[0] self.values = values[:] self.values_ = values_ = {} for key in values: values_[ key ] = key self.fast_validate = ( 10, values_, self.validate ) def validate ( self, object, name, value ): try: if value not in self.values_: match = None n = len( value ) for key in self.values: if value == key[:n]: if match is not None: match = None break match = key if match is None: self.error( object, name, value ) self.values_[ value ] = match return self.values_[ value ] except: self.error( object, name, value ) def info ( self ): return (' or '.join( [ repr( x ) for x in self.values ] ) + ' (or any unique prefix)') def get_editor ( self, trait ): from traitsui.api import EnumEditor return EnumEditor( values = self, cols = trait.cols or 3 ) def __getstate__ ( self ): result = self.__dict__.copy() if 'fast_validate' in result: del result[ 'fast_validate' ] return result #------------------------------------------------------------------------------- # 'TraitMap' class: #------------------------------------------------------------------------------- class TraitMap ( TraitHandler ): """Checks that the value assigned to a trait attribute is a key of a specified dictionary, and also assigns the dictionary value corresponding to that key to a *shadow* attribute. A trait attribute that uses a TraitMap handler is called *mapped* trait attribute. In practice, this means that the resulting object actually contains two attributes: one whose value is a key of the TraitMap dictionary, and the other whose value is the corresponding value of the TraitMap dictionary. The name of the shadow attribute is simply the base attribute name with an underscore ('_') appended. Mapped trait attributes can be used to allow a variety of user-friendly input values to be mapped to a set of internal, program-friendly values. Example ------- >>>class Person(HasTraits): ... married = Trait('yes', TraitMap({'yes': 1, 'no': 0 }) >>> >>>bob = Person() >>>print bob.married yes >>>print bob.married_ 1 In this example, the default value of the **married** attribute of the Person class is 'yes'. Because this attribute is defined using TraitPrefixList, instances of Person have another attribute, **married_**, whose default value is 1, the dictionary value corresponding to the key 'yes'. """ is_mapped = True def __init__ ( self, map ): """ Creates a TraitMap handler. Parameters ---------- map : dict A dictionary whose keys are valid values for the trait attribute, and whose corresponding values are the values for the shadow trait attribute. """ self.map = map self.fast_validate = ( 6, map ) def validate ( self, object, name, value ): try: if value in self.map: return value except: pass self.error( object, name, value ) def mapped_value ( self, value ): return self.map[ value ] def post_setattr ( self, object, name, value ): try: setattr( object, name + '_', self.mapped_value( value ) ) except: # We don't need a fancy error message, because this exception # should always be caught by a TraitCompound handler: raise TraitError, 'Unmappable' def info ( self ): keys = [ repr( x ) for x in self.map.keys() ] keys.sort() return ' or '.join( keys ) def get_editor ( self, trait ): from traitsui.api import EnumEditor return EnumEditor( values = self, cols = trait.cols or 3 ) #------------------------------------------------------------------------------- # 'TraitPrefixMap' class: #------------------------------------------------------------------------------- class TraitPrefixMap ( TraitMap ): """A cross between the TraitPrefixList and TraitMap classes. Like TraitMap, TraitPrefixMap is created using a dictionary, but in this case, the keys of the dictionary must be strings. Like TraitPrefixList, a string *v* is a valid value for the trait attribute if it is a prefix of one and only one key *k* in the dictionary. The actual values assigned to the trait attribute is *k*, and its corresponding mapped attribute is *map*[*k*]. Example ------- mapping = {'true': 1, 'yes': 1, 'false': 0, 'no': 0 } boolean_map = Trait('true', TraitPrefixMap(mapping)) This example defines a Boolean trait that accepts any prefix of 'true', 'yes', 'false', or 'no', and maps them to 1 or 0. """ def __init__ ( self, map ): """Creates a TraitPrefixMap handler. Parameters ---------- map : dict A dictionary whose keys are strings that are valid values for the trait attribute, and whose corresponding values are the values for the shadow trait attribute. """ self.map = map self._map = _map = {} for key in map.keys(): _map[ key ] = key self.fast_validate = ( 10, _map, self.validate ) def validate ( self, object, name, value ): try: if value not in self._map: match = None n = len( value ) for key in self.map.keys(): if value == key[:n]: if match is not None: match = None break match = key if match is None: self.error( object, name, value ) self._map[ value ] = match return self._map[ value ] except: self.error( object, name, value ) def info ( self ): return super( TraitPrefixMap, self ).info() + ' (or any unique prefix)' #------------------------------------------------------------------------------- # 'TraitExpression' class: #------------------------------------------------------------------------------- class TraitExpression ( TraitHandler ): """ Ensures that a value assigned to a trait attribute is a valid Python expression. The compiled form of a valid expression is stored as the mapped value of the trait. """ is_mapped = True def validate ( self, object, name, value ): try: compile( value, '', 'eval' ) return value except: self.error( object, name, value ) def post_setattr ( self, object, name, value ): object.__dict__[ name + '_' ] = self.mapped_value( value ) def info ( self ): return 'a valid Python expression' def mapped_value ( self, value ): return compile( value, '', 'eval' ) #------------------------------------------------------------------------------- # 'TraitCompound' class: #------------------------------------------------------------------------------- class TraitCompound ( TraitHandler ): """ Provides a logical-OR combination of other trait handlers. This class provides a means of creating complex trait definitions by combining several simpler trait definitions. TraitCompound is the underlying handler for the general forms of the Trait() function. A value is a valid value for a trait attribute based on a TraitCompound instance if the value is valid for at least one of the TraitHandler or trait objects supplied to the constructor. In addition, if at least one of the TraitHandler or trait objects is mapped (e.g., based on a TraitMap or TraitPrefixMap instance), then the TraitCompound is also mapped. In this case, any non-mapped traits or trait handlers use identity mapping. """ def __init__ ( self, *handlers ): """ Creates a TraitCompound handler. Parameters ---------- *handlers : list or tuple of TraitHandler or trait objects to be combined. """ if (len( handlers ) == 1) and (type( handlers[0] ) in SequenceTypes): handlers = handlers[0] self.handlers = handlers self.set_validate() def set_validate ( self ): self.is_mapped = False self.has_items = False self.reversable = True post_setattrs = [] mapped_handlers = [] validates = [] fast_validates = [] slow_validates = [] for handler in self.handlers: fv = getattr( handler, 'fast_validate', None ) if fv is not None: validates.append( handler.validate ) if fv[0] == 7: # If this is a nested complex fast validator, expand its # contents and adds its list to our list: fast_validates.extend( fv[1] ) else: # Else just add the entire validator to the list: fast_validates.append( fv ) else: slow_validates.append( handler.validate ) post_setattr = getattr( handler, 'post_setattr', None ) if post_setattr is not None: post_setattrs.append( post_setattr ) if handler.is_mapped: self.is_mapped = True mapped_handlers.append( handler ) else: self.reversable = False if handler.has_items: self.has_items = True self.validates = validates self.slow_validates = slow_validates if self.is_mapped: self.mapped_handlers = mapped_handlers elif hasattr( self, 'mapped_handlers' ): del self.mapped_handlers # If there are any fast validators, then we create a 'complex' fast # validator that composites them: if len( fast_validates ) > 0: # If there are any 'slow' validators, add a special handler at # the end of the fast validator list to handle them: if len( slow_validates ) > 0: fast_validates.append( ( 8, self ) ) # Create the 'complex' fast validator: self.fast_validate = ( 7, tuple( fast_validates ) ) elif hasattr( self, 'fast_validate' ): del self.fast_validate if len( post_setattrs ) > 0: self.post_setattrs = post_setattrs self.post_setattr = self._post_setattr elif hasattr( self, 'post_setattr' ): del self.post_setattr def validate ( self, object, name, value ): for validate in self.validates: try: return validate( object, name, value ) except TraitError: pass return self.slow_validate( object, name, value ) def slow_validate ( self, object, name, value ): for validate in self.slow_validates: try: return validate( object, name, value ) except TraitError: pass self.error( object, name, value ) def full_info ( self, object, name, value ): return ' or '.join( [ x.full_info( object, name, value ) for x in self.handlers ] ) def info ( self ): return ' or '.join( [ x.info() for x in self.handlers ] ) def mapped_value ( self, value ): for handler in self.mapped_handlers: try: return handler.mapped_value( value ) except: pass return value def _post_setattr ( self, object, name, value ): for post_setattr in self.post_setattrs: try: post_setattr( object, name, value ) return except TraitError: pass setattr( object, name + '_', value ) def get_editor ( self, trait ): from traitsui.api import TextEditor, CompoundEditor the_editors = [ x.get_editor( trait ) for x in self.handlers ] text_editor = TextEditor() count = 0 editors = [] for editor in the_editors: if isinstance( text_editor, editor.__class__ ): count += 1 if count > 1: continue editors.append( editor ) return CompoundEditor( editors = editors ) def items_event ( self ): return items_event() #------------------------------------------------------------------------------- # 'TraitTuple' class: #------------------------------------------------------------------------------- class TraitTuple ( TraitHandler ): """ Ensures that values assigned to a trait attribute are tuples of a specified length, with elements that are of specified types. TraitTuple is the underlying handler for the predefined trait **Tuple**, and the trait factory Tuple(). Example ------- rank = Range(1, 13) suit = Trait('Hearts', 'Diamonds', 'Spades', 'Clubs') class Card(HasTraits): value = Trait(TraitTuple(rank, suit)) This example defines a Card class, which has a **value** trait attribute, which must be a tuple of two elments. The first element must be an integer in the range from 1 to 13, and the second element must be one of the four strings, 'Hearts', 'Diamonds', 'Spades', or 'Clubs'. """ def __init__ ( self, *args ): """ Creates a TraitTuple handler. Parameters ---------- *args : A list of traits, each *trait*\ :sub:`i` specifies the type that the *i*\ th element of a tuple must be. Description ----------- Each *trait*\ :sub:`i` must be either a trait, or a value that can be converted to a trait using the Trait() function. The resulting trait handler accepts values that are tuples of the same length as *args*, and whose *i*\ th element is of the type specified by *trait*\ :sub:`i`. """ self.types = tuple( [ trait_from( arg ) for arg in args ] ) self.fast_validate = ( 9, self.types ) def validate ( self, object, name, value ): try: if isinstance( value, tuple ): types = self.types if len( value ) == len( types ): values = [] for i, type in enumerate( types ): values.append( type.handler.validate( object, name, value[i] ) ) return tuple( values ) except: pass self.error( object, name, value ) def full_info ( self, object, name, value ): return 'a tuple of the form: (%s)' % (', '.join( [ self._trait_info( type, object, name, value ) for type in self.types ] )) def _trait_info ( self, type, object, name, value ): handler = type.handler if handler is None: return 'any value' return handler.full_info( object, name, value ) def get_editor ( self, trait ): from traitsui.api import TupleEditor return TupleEditor( types = self.types, labels = trait.labels or [], cols = trait.cols or 1 ) #------------------------------------------------------------------------------- # 'TraitCallable' class: #------------------------------------------------------------------------------- class TraitCallable ( TraitHandler ): """Ensures that the value of a trait attribute is a callable Python object (usually a function or method). """ def validate ( self, object, name, value ): if (value is None) or callable( value ): return value self.error( object, name, value ) def info ( self ): return 'a callable value' #------------------------------------------------------------------------------- # 'TraitListEvent' class: #------------------------------------------------------------------------------- class TraitListEvent ( object ): #--------------------------------------------------------------------------- # Initialize the object: #--------------------------------------------------------------------------- def __init__ ( self, index = 0, removed = None, added = None ): self.index = index if removed is None: removed = [] self.removed = removed if added is None: added = [] self.added = added #------------------------------------------------------------------------------- # 'TraitList' class: #------------------------------------------------------------------------------- class TraitList ( TraitHandler ): """ Ensures that a value assigned to a trait attribute is a list containing elements of a specified type, and that the length of the list is also within a specified range. TraitList also makes sure that any changes made to the list after it is assigned to the trait attribute do not violate the list's type and length constraints. TraitList is the underlying handler for the predefined list-based traits. Example ------- class Card(HasTraits): pass class Hand(HasTraits): cards = Trait([], TraitList(Trait(Card), maxlen=52)) This example defines a Hand class, which has a **cards** trait attribute, which is a list of Card objects and can have from 0 to 52 items in the list. """ info_trait = None default_value_type = 5 _items_event = None def __init__ ( self, trait = None, minlen = 0, maxlen = sys.maxint, has_items = True ): """ Creates a TraitList handler. Parameters ---------- trait : Trait The type of items the list can contain. minlen : int The minimum length of the list. maxlen : int The maximum length of the list. has_items : bool Flag indicating whether the list contains elements. Description ----------- If *trait* is None or omitted, then no type checking is performed on any items in the list; otherwise, *trait* must be either a trait, or a value that can be converted to a trait using the Trait() function. """ self.item_trait = trait_from( trait ) self.minlen = max( 0, minlen ) self.maxlen = max( minlen, maxlen ) self.has_items = has_items def clone ( self ): return TraitList( self.item_trait, self.minlen, self.maxlen, self.has_items ) def validate ( self, object, name, value ): if (isinstance( value, list ) and (self.minlen <= len( value ) <= self.maxlen)): return TraitListObject( self, object, name, value ) self.error( object, name, value ) def full_info ( self, object, name, value ): if self.minlen == 0: if self.maxlen == sys.maxint: size = 'items' else: size = 'at most %d items' % self.maxlen else: if self.maxlen == sys.maxint: size = 'at least %d items' % self.minlen else: size = 'from %s to %s items' % ( self.minlen, self.maxlen ) handler = self.item_trait.handler if handler is None: info = '' else: info = ' which are %s' % handler.full_info( object, name, value ) return 'a list of %s%s' % ( size, info ) def get_editor ( self, trait ): handler = self.item_trait.handler if isinstance( handler, TraitInstance ) and (trait.mode != 'list'): from .api import HasTraits if issubclass( handler.aClass, HasTraits ): try: object = handler.aClass() from traitsui.table_column import ObjectColumn from traitsui.table_filter import (EvalFilterTemplate, RuleFilterTemplate, MenuFilterTemplate, EvalTableFilter) from traitsui.api import TableEditor return TableEditor( columns = [ ObjectColumn( name = name ) for name in object.editable_traits() ], filters = [ RuleFilterTemplate, MenuFilterTemplate, EvalFilterTemplate ], edit_view = '', orientation = 'vertical', search = EvalTableFilter(), deletable = True, row_factory = handler.aClass ) except: pass from traitsui.api import ListEditor return ListEditor( trait_handler = self, rows = trait.rows or 5, use_notebook = trait.use_notebook is True, page_name = trait.page_name or '' ) def items_event ( self ): return items_event() def items_event ( ): if TraitList._items_event is None: TraitList._items_event = \ Event( TraitListEvent, is_base = False ).as_ctrait() return TraitList._items_event #------------------------------------------------------------------------------- # 'TraitListObject' class: #------------------------------------------------------------------------------- class TraitListObject ( list ): def __init__ ( self, trait, object, name, value ): self.trait = trait self.object = ref( object ) self.name = name self.name_items = None if trait.has_items: self.name_items = name + '_items' # Do the validated 'setslice' assignment without raising an # 'items_changed' event: if trait.minlen <= len( value ) <= trait.maxlen: try: validate = trait.item_trait.handler.validate if validate is not None: value = [ validate( object, name, val ) for val in value ] list.__setitem__(self, slice(0, 0), value ) return except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp self.len_error( len( value ) ) def _send_trait_items_event(self, name, event, items_event=None): """ Send a TraitListEvent to the owning object if there is one. """ object = self.object() if object is not None: if items_event is None and hasattr(self, 'trait'): items_event = self.trait.items_event() object.trait_items_event(name, event, items_event) def __deepcopy__ ( self, memo ): id_self = id( self ) if id_self in memo: return memo[ id_self ] memo[ id_self ] = result = TraitListObject( self.trait, lambda: None, self.name, [ copy.deepcopy( x, memo ) for x in self ] ) return result def __setitem__ ( self, key, value ): self_trait = getattr(self, 'trait', None) if self_trait is None: return list.__setitem__(self, key, value) try: removed = self[ key ] except: removed = [] try: object = self.object() validate = self.trait.item_trait.handler.validate name = self.name if isinstance(key, slice): values = value slice_len = len(removed) delta = len( values ) - slice_len step = 1 if key.step is None else key.step if step != 1 and delta != 0: raise ValueError( 'attempt to assign sequence of size %d to extended slice of size %d' % ( len( values ), slice_len )) newlen = (len(self) + delta) if not (self_trait.minlen <= newlen <= self_trait.maxlen): self.len_error( newlen ) return if validate is not None: values = [ validate( object, name, value ) for value in values ] value = values if step == 1: # FIXME: Bug-for-bug compatibility with old __setslice__ code. # In this case, we return a TraitListEvent with an # index=key.start and the removed and added lists as they # are. index = key.start else: # Otherwise, we have an extended slice which was handled, # badly, by __setitem__ before. In this case, we return the # removed and added lists wrapped in another list. index = key values = [values] removed = [removed] else: if validate is not None: value = validate( object, name, value ) values = [ value ] removed = [ removed ] delta = 0 index = len( self ) + key if key < 0 else key list.__setitem__( self, key, value ) if self.name_items is not None: if delta == 0: try: if removed == values: return except: # Treat incomparable values as equal: pass self._send_trait_items_event( self.name_items, TraitListEvent( index, removed, values ) ) except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp if sys.version_info[0] < 3: def __setslice__ ( self, i, j, values ): self.__setitem__(slice(i,j), values) def __delitem__ ( self, key ): trait = getattr(self, 'trait', None) if trait is None: return list.__delitem__(self, key) try: removed = self[ key ] except: removed = [] if isinstance(key,slice): slice_len = len(removed) delta = slice_len step = 1 if key.step is None else key.step if step == 1: # FIXME: See corresponding comment in __setitem__() for # explanation. index = key.start else: index = key removed = [removed] else: delta = 1 index = len( self ) + key + 1 if key < 0 else key removed = [ removed ] if not (trait.minlen <= (len( self ) - delta)): self.len_error( len( self ) - delta) return list.__delitem__( self, key ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitListEvent( index, removed ) ) if sys.version_info[0] < 3: def __delslice__ ( self, i, j ): self.__delitem__(slice(i,j)) def __iadd__(self, other): self.extend(other) return self def __imul__(self, count): trait = getattr( self, 'trait', None ) if trait is None: return list.__imul__( self, count ) original_len = len( self ) if trait.minlen <= original_len * count <= trait.maxlen: if self.name_items is not None: removed = None if count else self[:] result = list.__imul__(self, count) if self.name_items is not None: added = self[original_len:] if count else None index = original_len if count else 0 self._send_trait_items_event( self.name_items, TraitListEvent( index, removed, added ) ) return result else: self.len_error( original_len * count ) def append ( self, value ): trait = getattr( self, 'trait', None ) if trait is None: list.append( self, value ) return if trait.minlen <= (len( self ) + 1) <= trait.maxlen: try: validate = trait.item_trait.handler.validate object = self.object() if validate is not None: value = validate( object, self.name, value ) list.append( self, value ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitListEvent( len( self ) - 1, None, [ value ] ), trait.items_event() ) return except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp self.len_error( len( self ) + 1 ) def insert ( self, index, value ): trait = getattr( self, 'trait', None ) if trait is None: return list.insert(self, index, value) if trait.minlen <= (len( self ) + 1) <= trait.maxlen: try: validate = trait.item_trait.handler.validate object = self.object() if validate is not None: value = validate( object, self.name, value ) list.insert( self, index, value ) if self.name_items is not None: # Length before the insertion. original_len = len( self ) - 1 # Indices outside [-original_len, original_len] are clipped. # This matches the behaviour of insert on the # underlying list. if index < 0: index += original_len if index < 0: index = 0 elif index > original_len: index = original_len self._send_trait_items_event( self.name_items, TraitListEvent( index, None, [ value ] ), trait.items_event() ) return except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp self.len_error( len( self ) + 1 ) def extend ( self, xlist ): trait = getattr( self, 'trait', None ) if trait is None: list.extend( self, xlist ) return try: len_xlist = len( xlist ) except: raise TypeError, "list.extend() argument must be iterable" if (trait.minlen <= (len( self ) + len_xlist) <= trait.maxlen): object = self.object() name = self.name validate = trait.item_trait.handler.validate try: if validate is not None: xlist = [ validate( object, name, value ) for value in xlist ] list.extend( self, xlist ) if (self.name_items is not None) and (len( xlist ) != 0): self._send_trait_items_event( self.name_items, TraitListEvent( len( self ) - len( xlist ), None, xlist ), trait.items_event() ) return except TraitError, excp: excp.set_prefix( 'The elements of the' ) raise excp self.len_error( len( self ) + len( xlist ) ) def remove ( self, value ): trait = getattr(self, 'trait', None) if trait is None: list.remove(self, value) return if trait.minlen < len( self ): try: index = self.index( value ) removed = [ self[ index ] ] except: pass list.remove( self, value ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitListEvent( index, removed ) ) elif len(self) == 0: # Let whatever system error (ValueError) should be raised be raised. list.remove(self, value) else: self.len_error( len( self ) - 1 ) if sys.version_info[0] < 3: def sort ( self, cmp = None, key = None, reverse = False ): removed = self[:] list.sort( self, cmp = cmp, key = key, reverse = reverse ) self._sort_common(removed) else: def sort ( self, key = None, reverse = False ): removed = self[:] list.sort( self, key = key, reverse = reverse ) self._sort_common(removed) def _sort_common ( self, removed ): if (getattr(self, 'name_items', None) is not None and getattr(self, 'trait', None) is not None): self._send_trait_items_event( self.name_items, TraitListEvent( 0, removed, self[:] ) ) def reverse ( self ): removed = self[:] if len( self ) > 1: list.reverse( self ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitListEvent( 0, removed, self[:] ) ) def pop ( self, *args ): if not hasattr(self, 'trait'): return list.pop(self, *args) if self.trait.minlen < len( self ): if len( args ) > 0: index = args[0] else: index = -1 try: removed = [ self[ index ] ] except: pass result = list.pop( self, *args ) if self.name_items is not None: if index < 0: index = len( self ) + index + 1 self._send_trait_items_event( self.name_items, TraitListEvent( index, removed ) ) return result else: self.len_error( len( self ) - 1 ) def rename ( self, name ): trait = self.object()._trait( name, 0 ) if trait is not None: self.name = name self.trait = trait.handler def len_error ( self, len ): raise TraitError( "The '%s' trait of %s instance must be %s, " "but you attempted to change its length to %d element%s." % ( self.name, class_of( self.object() ), self.trait.full_info( self.object(), self.name, Undefined ), len, 's'[ len == 1: ] ) ) def __getstate__ ( self ): result = self.__dict__.copy() result.pop('object', None) result.pop('trait', None) return result def __setstate__ ( self, state ): name = state.setdefault('name', '') object = state.pop( 'object', None ) if object is not None: self.object = ref( object ) self.rename( name ) else: self.object = lambda: None self.__dict__.update( state ) #------------------------------------------------------------------------------- # 'TraitSetEvent' class: #------------------------------------------------------------------------------- class TraitSetEvent ( object ): #--------------------------------------------------------------------------- # Initialize the object: #--------------------------------------------------------------------------- def __init__ ( self, removed = None, added = None ): if removed is None: removed = set() self.removed = removed if added is None: added = set() self.added = added #------------------------------------------------------------------------------- # 'TraitSetObject' class: #------------------------------------------------------------------------------- class TraitSetObject ( set ): def __init__ ( self, trait, object, name, value ): self.trait = trait self.object = ref( object ) self.name = name self.name_items = None if trait.has_items: self.name_items = name + '_items' # Validate and assign the initial set value: try: validate = trait.item_trait.handler.validate if validate is not None: value = [ validate( object, name, val ) for val in value ] super( TraitSetObject, self ).__init__( value ) return except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp def _send_trait_items_event(self, name, event, items_event=None): """ Send a TraitDictEvent to the owning object if there is one. """ object = self.object() if object is not None: if items_event is None and hasattr(self, 'trait'): items_event = self.trait.items_event() object.trait_items_event(name, event, items_event) def __deepcopy__ ( self, memo ): id_self = id( self ) if id_self in memo: return memo[ id_self ] memo[ id_self ] = result = TraitSetObject( self.trait, lambda: None, self.name, [ copy.deepcopy( x, memo ) for x in self ] ) return result def update ( self, value ): if not hasattr(self, 'trait'): return set.update(self, value) try: if not isinstance(value, set): value = set(value) added = value.difference( self ) if len( added ) > 0: object = self.object() validate = self.trait.item_trait.handler.validate if validate is not None: name = self.name added = set( [ validate( object, name, item ) for item in added ] ) set.update( self, added ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( None, added ) ) except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp def intersection_update ( self, value ): removed = self.difference( value ) if len( removed ) > 0: set.difference_update( self, removed ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( removed ) ) def difference_update ( self, value ): removed = self.intersection( value ) if len( removed ) > 0: set.difference_update( self, removed ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( removed ) ) def symmetric_difference_update ( self, value ): if not hasattr(self, 'trait'): return set.symmetric_difference_update(self, value) if not isinstance(value, set): value = set(value) removed = self.intersection( value ) added = value.difference( self ) if (len( removed ) > 0) or (len( added ) > 0): object = self.object() set.difference_update( self, removed ) if len( added ) > 0: validate = self.trait.item_trait.handler.validate if validate is not None: name = self.name added = set( [ validate( object, name, item ) for item in added ] ) set.update( self, added ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( removed, added ) ) def add ( self, value ): if not hasattr(self, 'trait'): return set.add(self, value) if value not in self: try: object = self.object() validate = self.trait.item_trait.handler.validate if validate is not None: value = validate( object, self.name, value ) set.add( self, value ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( None, set( [ value ] ) ) ) except TraitError, excp: excp.set_prefix( 'Each element of the' ) raise excp def remove ( self, value ): set.remove( self, value ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( set( [ value ] ) ) ) def discard ( self, value ): if value in self: self.remove( value ) def pop ( self ): value = set.pop( self ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( set( [ value ] ) ) ) return value def clear ( self ): removed = set( self ) set.clear( self ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitSetEvent( removed ) ) def copy ( self ): """ Return a true ``set`` object with a copy of the data. """ return set(self) def __reduce_ex__(self, protocol=None): """ Overridden to make sure we call our custom __getstate__. """ return (copy_reg._reconstructor, (type(self), set, list(self)), self.__getstate__()) def __getstate__ ( self ): result = self.__dict__.copy() result.pop('object', None) result.pop('trait', None) return result def __setstate__ ( self, state ): name = state.setdefault('name', '') object = state.pop( 'object', None ) if object is not None: self.object = ref( object ) self.rename( name ) else: self.object = lambda: None self.__dict__.update( state ) def __ior__(self, value): self.update(value) return self def __iand__(self, value): self.intersection_update(value) return self def __ixor__(self, value): self.symmetric_difference_update(value) return self def __isub__(self, value): self.difference_update(value) return self #------------------------------------------------------------------------------- # 'TraitDictEvent' class: #------------------------------------------------------------------------------- class TraitDictEvent ( object ): def __init__ ( self, added = None, changed = None, removed = None ): """ Parameters ---------- added : dict New keys and values. changed : dict Updated keys and their previous values. removed : dict Old keys and values that were just removed. """ # Construct new empty dicts every time instead of using a default value # in the method argument, just in case someone gets the bright idea of # modifying the dict they get in-place. if added is None: added = {} self.added = added if changed is None: changed = {} self.changed = changed if removed is None: removed = {} self.removed = removed #------------------------------------------------------------------------------- # 'TraitDict' class: #------------------------------------------------------------------------------- class TraitDict ( TraitHandler ): """ Ensures that values assigned to a trait attribute are dictionaries whose keys and values are of specified types. TraitDict also makes sure that any changes to keys or values made that are made after the dictionary is assigned to the trait attribute satisfy the type constraints. TraitDict is the underlying handler for the dictionary-based predefined traits, and the Dict() trait factory. Example ------- class WorkoutClass(HasTraits): member_weights = Trait({}, TraitDict(str, float)) This example defines a WorkoutClass class containing a *member_weights* trait attribute whose value must be a dictionary containing keys that are strings (i.e., the members' names) and whose associated values must be floats (i.e., their most recently recorded weight). """ info_trait = None default_value_type = 6 _items_event = None def __init__ ( self, key_trait = None, value_trait = None, has_items = True ): """ Creates a TraitDict handler. Parameters ---------- key_trait : trait The type for the dictionary keys. value_trait : trait The type for the dictionary values. has_items : bool Flag indicating whether the dictionary contains entries. Description ----------- If *key_trait* is None or omitted, the keys in the dictionary can be of any type. Otherwise, *key_trait* must be either a trait, or a value that can be converted to a trait using the Trait() function. In this case, all dictionary keys are checked to ensure that they are of the type specified by *key_trait*. If *value_trait* is None or omitted, the values in the dictionary can be of any type. Otherwise, *value_trait* must be either a trait, or a value that can be converted to a trait using the Trait() function. In this case, all dictionary values are checked to ensure that they are of the type specified by *value_trait*. """ self.key_trait = trait_from( key_trait ) self.value_trait = trait_from( value_trait ) self.has_items = has_items handler = self.value_trait.handler if handler.has_items: handler = handler.clone() handler.has_items = False self.value_handler = handler def clone ( self ): return TraitDict( self.key_trait, self.value_trait, self.has_items ) def validate ( self, object, name, value ): if isinstance( value, dict ): return TraitDictObject( self, object, name, value ) self.error( object, name, value ) def full_info ( self, object, name, value ): extra = '' handler = self.key_trait.handler if handler is not None: extra = (' with keys which are %s' % handler.full_info( object, name, value)) handler = self.value_handler if handler is not None: if extra == '': extra = ' with' else: extra += ' and' extra += (' values which are %s' % handler.full_info( object, name, value )) return 'a dictionary%s' % extra def get_editor ( self, trait ): if self.editor is None: from traitsui.api import TextEditor self.editor = TextEditor( evaluate = eval ) return self.editor def items_event ( self ): if TraitDict._items_event is None: TraitDict._items_event = \ Event( TraitDictEvent, is_base = False ).as_ctrait() return TraitDict._items_event #------------------------------------------------------------------------------- # 'TraitDictObject' class: #------------------------------------------------------------------------------- class TraitDictObject ( dict ): def __init__ ( self, trait, object, name, value ): self.trait = trait self.object = ref( object ) self.name = name self.name_items = None if trait.has_items: self.name_items = name + '_items' if len( value ) > 0: dict.update( self, self._validate_dic( value ) ) def _send_trait_items_event(self, name, event, items_event=None): """ Send a TraitDictEvent to the owning object if there is one. """ object = self.object() if object is not None: if items_event is None and hasattr(self, 'trait'): items_event = self.trait.items_event() object.trait_items_event(name, event, items_event) def __deepcopy__ ( self, memo ): id_self = id( self ) if id_self in memo: return memo[ id_self ] memo[ id_self ] = result = TraitDictObject( self.trait, lambda: None, self.name, dict([ copy.deepcopy( x, memo ) for x in self.iteritems() ]) ) return result def __setitem__ ( self, key, value ): trait = getattr( self, 'trait', None ) if trait is None: dict.__setitem__( self, key, value ) return object = self.object() try: validate = trait.key_trait.handler.validate if validate is not None: key = validate( object, self.name, key ) except TraitError, excp: excp.set_prefix( 'Each key of the' ) raise excp try: validate = trait.value_handler.validate if validate is not None: value = validate( object, self.name, value ) if self.name_items is not None: if key in self: added = None old = self[ key ] changed = { key: old } else: added = { key: value } changed = None dict.__setitem__( self, key, value ) if self.name_items is not None: if added is None: try: if old == value: return except: # Treat incomparable objects as unequal: pass self._send_trait_items_event( self.name_items, TraitDictEvent( added, changed ), trait.items_event() ) except TraitError, excp: excp.set_prefix( 'Each value of the' ) raise excp def __delitem__ ( self, key ): if self.name_items is not None: removed = { key: self[ key ] } dict.__delitem__( self, key ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitDictEvent( removed = removed ) ) def clear ( self ): if len( self ) > 0: if self.name_items is not None: removed = self.copy() dict.clear( self ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitDictEvent( removed = removed ) ) def update ( self, dic ): trait = getattr( self, 'trait', None ) if trait is None: dict.update( self, dic ) return if len( dic ) > 0: new_dic = self._validate_dic( dic ) if self.name_items is not None: added = {} changed = {} for key, value in new_dic.iteritems(): if key in self: changed[ key ] = self[ key ] else: added[ key ] = value dict.update( self, new_dic ) self._send_trait_items_event( self.name_items, TraitDictEvent( added = added, changed = changed ) ) else: dict.update( self, new_dic ) def setdefault ( self, key, value = None ): if key in self: return self[ key ] self[ key ] = value result = self[ key ] if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitDictEvent( added = { key: result } ) ) return result def pop ( self, key, value = Undefined ): if (value is Undefined) or key in self: result = dict.pop( self, key ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitDictEvent( removed = { key: result } ) ) return result return value def popitem ( self ): result = dict.popitem( self ) if self.name_items is not None: self._send_trait_items_event( self.name_items, TraitDictEvent( removed = { result[0]: result[1] } ) ) return result def rename ( self, name ): trait = self.object()._trait( name, 0 ) if trait is not None: self.name = name self.trait = trait.handler else: logger.debug( "rename: No 'trait' in %s for '%s'" % ( self.object(), name ) ) def __getstate__ ( self ): result = self.__dict__.copy() result.pop('object', None) result.pop('trait', None) return result def __setstate__ ( self, state ): name = state.setdefault('name', '') object = state.pop( 'object', None ) if object is not None: self.object = ref( object ) self.rename( name ) else: self.object = lambda: None self.__dict__.update( state ) #-- Private Methods ------------------------------------------------------------ def _validate_dic ( self, dic ): name = self.name new_dic = {} key_validate = self.trait.key_trait.handler.validate if key_validate is None: key_validate = lambda object, name, key: key value_validate = self.trait.value_trait.handler.validate if value_validate is None: value_validate = lambda object, name, value: value object = self.object() for key, value in dic.iteritems(): try: key = key_validate( object, name, key ) except TraitError, excp: excp.set_prefix( 'Each key of the' ) raise excp try: value = value_validate( object, name, value ) except TraitError, excp: excp.set_prefix( 'Each value of the' ) raise excp new_dic[ key ] = value return new_dic #------------------------------------------------------------------------------- # Tell the C-based traits module about 'TraitListObject', 'TraitSetObject and # 'TraitDictObject', and the PyProtocols 'adapt' function: #------------------------------------------------------------------------------- from . import ctraits ctraits._list_classes( TraitListObject, TraitSetObject, TraitDictObject ) def _adapt_wrapper(*args, **kw): # We need this wrapper to defer the import of 'adapt' and avoid a circular # import. The ctraits 'adapt' callback needs to be set as soon as possible, # but the adaptation mechanism relies on traits. # This wrapper is called once, after which we set the ctraits callback # to point directly to 'adapt'. from traits.adaptation.api import adapt ctraits._adapt(adapt) return adapt(*args, **kw) ctraits._adapt( _adapt_wrapper ) traits-4.6.0/traits/trait_notifiers.py000066400000000000000000000643231300633736300201300ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005-2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Original Date: 06/21/2002 # #------------------------------------------------------------------------------ """ Classes that implement and support the Traits change notification mechanism """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import from threading import local as thread_local from threading import Thread from thread import get_ident import traceback from types import MethodType import weakref import sys from .trait_base import Uninitialized from .trait_errors import TraitNotificationError #------------------------------------------------------------------------------- # Global Data: #------------------------------------------------------------------------------- # The thread ID for the user interface thread ui_thread = -1 # The handler for notifications that must be run on the UI thread ui_handler = None #------------------------------------------------------------------------------- # Sets up the user interface thread handler: #------------------------------------------------------------------------------- def set_ui_handler ( handler ): """ Sets up the user interface thread handler. """ global ui_handler, ui_thread ui_handler = handler ui_thread = get_ident() def ui_dispatch( handler, *args, **kw ): if get_ident() == ui_thread: handler( *args, **kw ) else: ui_handler( handler, *args, **kw ) #------------------------------------------------------------------------------- # 'NotificationExceptionHandlerState' class: #------------------------------------------------------------------------------- class NotificationExceptionHandlerState ( object ): def __init__ ( self, handler, reraise_exceptions, locked ): self.handler = handler self.reraise_exceptions = reraise_exceptions self.locked = locked #------------------------------------------------------------------------------- # 'NotificationExceptionHandler' class: #------------------------------------------------------------------------------- class NotificationExceptionHandler ( object ): def __init__ ( self ): self.traits_logger = None self.main_thread = None self.thread_local = thread_local() #-- Private Methods ------------------------------------------------------------ def _push_handler ( self, handler = None, reraise_exceptions = False, main = False, locked = False ): """ Pushes a new traits notification exception handler onto the stack, making it the new exception handler. Returns a NotificationExceptionHandlerState object describing the previous exception handler. Parameters ---------- handler : handler The new exception handler, which should be a callable or None. If None (the default), then the default traits notification exception handler is used. If *handler* is not None, then it must be a callable which can accept four arguments: object, trait_name, old_value, new_value. reraise_exceptions : bool Indicates whether exceptions should be reraised after the exception handler has executed. If True, exceptions will be re-raised after the specified handler has been executed. The default value is False. main : bool Indicates whether the caller represents the main application thread. If True, then the caller's exception handler is made the default handler for any other threads that are created. Note that a thread can explicitly set its own exception handler if desired. The *main* flag is provided to make it easier to set a global application policy without having to explicitly set it for each thread. The default value is False. locked : bool Indicates whether further changes to the Traits notification exception handler state should be allowed. If True, then any subsequent calls to _push_handler() or _pop_handler() for that thread will raise a TraitNotificationError. The default value is False. """ handlers = self._get_handlers() self._check_lock( handlers ) if handler is None: handler = self._log_exception handlers.append( NotificationExceptionHandlerState( handler, reraise_exceptions, locked ) ) if main: self.main_thread = handlers return handlers[-2] def _pop_handler ( self ): """ Pops the traits notification exception handler stack, restoring the exception handler in effect prior to the most recent _push_handler() call. If the stack is empty or locked, a TraitNotificationError exception is raised. Note that each thread has its own independent stack. See the description of the _push_handler() method for more information on this. """ handlers = self._get_handlers() self._check_lock( handlers ) if len( handlers ) > 1: handlers.pop() else: raise TraitNotificationError( 'Attempted to pop an empty traits notification exception ' 'handler stack.' ) def _handle_exception ( self, object, trait_name, old, new ): """ Handles a traits notification exception using the handler defined by the topmost stack entry for the corresponding thread. """ excp_class, excp = sys.exc_info()[:2] handler_info = self._get_handlers()[-1] handler_info.handler( object, trait_name, old, new ) if (handler_info.reraise_exceptions or isinstance( excp, TraitNotificationError )): raise def _get_handlers ( self ): """ Returns the handler stack associated with the currently executing thread. """ thread_local = self.thread_local if isinstance( thread_local, dict ): id = get_ident() handlers = thread_local.get( id ) else: handlers = getattr( thread_local, 'handlers', None ) if handlers is None: if self.main_thread is not None: handler = self.main_thread[-1] else: handler = NotificationExceptionHandlerState( self._log_exception, False, False ) handlers = [ handler ] if isinstance( thread_local, dict ): thread_local[ id ] = handlers else: thread_local.handlers = handlers return handlers def _check_lock ( self, handlers ): """ Raises an exception if the specified handler stack is locked. """ if handlers[-1].locked: raise TraitNotificationError( 'The traits notification exception handler is locked. ' 'No changes are allowed.' ) #--------------------------------------------------------------------------- # This method defines the default notification exception handling # behavior of traits. However, it can be completely overridden by pushing # a new handler using the '_push_handler' method. # # It logs any exceptions generated in a trait notification handler. #--------------------------------------------------------------------------- def _log_exception ( self, object, trait_name, old, new ): """ Logs any exceptions generated in a trait notification handler. """ # When the stack depth is too great, the logger can't always log the # message. Make sure that it goes to the console at a minimum: excp_class, excp = sys.exc_info()[:2] if ((excp_class is RuntimeError) and (len(excp.args) > 0) and (excp.args[0] == 'maximum recursion depth exceeded')): sys.__stderr__.write( 'Exception occurred in traits notification ' 'handler for object: %s, trait: %s, old value: %s, ' 'new value: %s.\n%s\n' % ( object, trait_name, old, new, ''.join( traceback.format_exception( *sys.exc_info() ) ) ) ) logger = self.traits_logger if logger is None: import logging self.traits_logger = logger = logging.getLogger( 'traits' ) handler = logging.StreamHandler() handler.setFormatter( logging.Formatter( '%(message)s' ) ) logger.addHandler( handler ) print ('Exception occurred in traits notification handler.\n' 'Please check the log file for details.') try: logger.exception( 'Exception occurred in traits notification handler for ' 'object: %s, trait: %s, old value: %s, new value: %s' % ( object, trait_name, old, new ) ) except Exception: # Ignore anything we can't log the above way: pass #------------------------------------------------------------------------------- # Traits global notification exception handler: #------------------------------------------------------------------------------- notification_exception_handler = NotificationExceptionHandler() push_exception_handler = notification_exception_handler._push_handler pop_exception_handler = notification_exception_handler._pop_handler handle_exception = notification_exception_handler._handle_exception #------------------------------------------------------------------------------- # Traits global notification event tracer: #------------------------------------------------------------------------------- _pre_change_event_tracer = None _post_change_event_tracer = None def set_change_event_tracers( pre_tracer=None, post_tracer=None ): """ Set the global trait change event tracers. The global tracers are called whenever a trait change event is dispatched. There are two tracers: `pre_tracer` is called before the notification is sent; `post_tracer` is called after the notification is sent, even if the notification failed with an exception (in which case the `post_tracer` is called with a reference to the exception, then the exception is sent to the `notification_exception_handler`). The tracers should be a callable taking 5 arguments: :: tracer(obj, trait_name, old, new, handler) `obj` is the source object, on which trait `trait_name` was changed from value `old` to value `new`. `handler` is the function or method that will be notified of the change. The post-notification tracer also has a keyword argument, `exception`, that is `None` if no exception has been raised, and the a reference to the raise exception otherwise. :: post_tracer(obj, trait_name, old, new, handler, exception=None) Note that for static trait change listeners, `handler` is not a method, but rather the function before class creation, since this is the way Traits works at the moment. """ global _pre_change_event_tracer global _post_change_event_tracer _pre_change_event_tracer = pre_tracer _post_change_event_tracer = post_tracer def clear_change_event_tracers(): """ Clear the global trait change event tracer. """ global _pre_change_event_tracer global _post_change_event_tracer _pre_change_event_tracer = None _post_change_event_tracer = None #------------------------------------------------------------------------------- # 'AbstractStaticChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class AbstractStaticChangeNotifyWrapper(object): """ Concrete implementation must define the 'argument_transforms' class argument, a dictionary mapping the number of arguments in the event handler to a function that takes the arguments (obj, trait_name, old, new) and returns the arguments tuple for the actual handler. """ arguments_transforms = {} def __init__ ( self, handler ): arg_count = handler.func_code.co_argcount if arg_count > 4: raise TraitNotificationError( ('Invalid number of arguments for the static anytrait change ' 'notification handler: %s. A maximum of 4 arguments is ' 'allowed, but %s were specified.') % ( handler.__name__, arg_count ) ) self.argument_transform = self.argument_transforms[arg_count] self.handler = handler def __call__ ( self, object, trait_name, old, new ): """ Dispatch to the appropriate handler method. """ if old is not Uninitialized: # Extract the arguments needed from the handler. args = self.argument_transform( object, trait_name, old, new ) # Send a description of the change event to the event tracer. if _pre_change_event_tracer is not None: _pre_change_event_tracer( object, trait_name, old, new, self.handler ) try: # Call the handler. self.handler( *args ) except Exception as e: if _post_change_event_tracer is not None: _post_change_event_tracer( object, trait_name, old, new, self.handler, exception=e ) handle_exception( object, trait_name, old, new ) else: if _post_change_event_tracer is not None: _post_change_event_tracer( object, trait_name, old, new, self.handler, exception=None ) def equals ( self, handler ): return False #------------------------------------------------------------------------------- # 'StaticAnyTraitChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class StaticAnyTraitChangeNotifyWrapper(AbstractStaticChangeNotifyWrapper): # The wrapper is called with the full set of argument, and we need to # create a tuple with the arguments that need to be sent to the event # handler, depending on the number of those. argument_transforms = { 0: lambda obj, name, old, new: (), 1: lambda obj, name, old, new: (obj,), 2: lambda obj, name, old, new: (obj, name), 3: lambda obj, name, old, new: (obj, name, new), 4: lambda obj, name, old, new: (obj, name, old, new), } #------------------------------------------------------------------------------- # 'StaticTraitChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class StaticTraitChangeNotifyWrapper(AbstractStaticChangeNotifyWrapper): # The wrapper is called with the full set of argument, and we need to # create a tuple with the arguments that need to be sent to the event # handler, depending on the number of those. argument_transforms = { 0: lambda obj, name, old, new: (), 1: lambda obj, name, old, new: (obj,), 2: lambda obj, name, old, new: (obj, new), 3: lambda obj, name, old, new: (obj, old, new), 4: lambda obj, name, old, new: (obj, name, old, new), } #------------------------------------------------------------------------------- # 'TraitChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class TraitChangeNotifyWrapper(object): """ Dynamic change notify wrapper. This class is in charge to dispatch trait change events to dynamic listener, typically created using the `on_trait_change` method, or the decorator with the same name. """ # The wrapper is called with the full set of argument, and we need to # create a tuple with the arguments that need to be sent to the event # handler, depending on the number of those. argument_transforms = { 0: lambda obj, name, old, new: (), 1: lambda obj, name, old, new: (new,), 2: lambda obj, name, old, new: (name, new), 3: lambda obj, name, old, new: (obj, name, new), 4: lambda obj, name, old, new: (obj, name, old, new), } def __init__ ( self, handler, owner, target=None ): self.init( handler, owner, target ) def init ( self, handler, owner, target=None ): # If target is not None and handler is a function then the handler # will be removed when target is deleted. if type( handler ) is MethodType: func = handler.im_func object = handler.im_self if object is not None: self.object = weakref.ref( object, self.listener_deleted ) self.name = handler.__name__ self.owner = owner arg_count = func.func_code.co_argcount - 1 if arg_count > 4: raise TraitNotificationError( ('Invalid number of arguments for the dynamic trait ' 'change notification handler: %s. A maximum of 4 ' 'arguments is allowed, but %s were specified.') % ( func.__name__, arg_count ) ) # We use the unbound method here to prevent cyclic garbage # (issue #100). self.notify_listener = type(self)._notify_method_listener self.argument_transform = self.argument_transforms[arg_count] return arg_count elif target is not None: # Set up so the handler will be removed when the target is deleted. self.object = weakref.ref( target, self.listener_deleted ) self.owner = owner arg_count = handler.func_code.co_argcount if arg_count > 4: raise TraitNotificationError( ('Invalid number of arguments for the dynamic trait change ' 'notification handler: %s. A maximum of 4 arguments is ' 'allowed, but %s were specified.') % ( handler.__name__, arg_count ) ) self.name = None self.handler = handler # We use the unbound method here to prevent cyclic garbage # (issue #100). self.notify_listener = type(self)._notify_function_listener self.argument_transform = self.argument_transforms[arg_count] return arg_count def __call__(self, object, trait_name, old, new): """ Dispatch to the appropriate method. We do explicit dispatch instead of assigning to the .__call__ instance attribute to avoid reference cycles. """ # `notify_listener` is either the *unbound* # `_notify_method_listener` or `_notify_function_listener` to # prevent cyclic garbage (issue #100). self.notify_listener( self, object, trait_name, old, new ) def dispatch ( self, handler, *args ): """ Dispatch the event to the listener. This method is normally the only one that needs to be overridden in a subclass to implement the subclass's dispatch mechanism. """ handler( *args ) def equals ( self, handler ): if handler is self: return True if (type( handler ) is MethodType) and (handler.im_self is not None): return ((handler.__name__ == self.name) and (handler.im_self is self.object())) return ((self.name is None) and (handler == self.handler)) def listener_deleted ( self, ref ): # In multithreaded situations, it's possible for this method to # be called after, or concurrently with, the dispose method. # Don't raise in that case. try: self.owner.remove( self ) except ValueError: pass self.object = self.owner = None def dispose ( self ): self.object = None def _dispatch_change_event(self, object, trait_name, old, new, handler): """ Prepare and dispatch a trait change event to a listener. """ # Extract the arguments needed from the handler. args = self.argument_transform( object, trait_name, old, new ) # Send a description of the event to the change event tracer. if _pre_change_event_tracer is not None: _pre_change_event_tracer( object, trait_name, old, new, handler ) # Dispatch the event to the listener. try: self.dispatch( handler, *args ) except Exception as e: if _post_change_event_tracer is not None: _post_change_event_tracer( object, trait_name, old, new, handler, exception=e ) # This call needs to be made inside the `except` block in case # the handler wants to re-raise the exception. handle_exception( object, trait_name, old, new ) else: if _post_change_event_tracer is not None: _post_change_event_tracer( object, trait_name, old, new, handler, exception=None ) def _notify_method_listener(self, object, trait_name, old, new): """ Dispatch a trait change event to a method listener. """ obj_weak_ref = self.object if (obj_weak_ref is not None) and (old is not Uninitialized): # We make sure to hold a reference to the object before invoking # `getattr` so that the listener does not disappear in a # multi-threaded case. obj = obj_weak_ref() if obj is not None: # Dynamically resolve the listener by name. listener = getattr( obj, self.name ) self._dispatch_change_event( object, trait_name, old, new, listener ) def _notify_function_listener(self, object, trait_name, old, new): """ Dispatch a trait change event to a function listener. """ if old is not Uninitialized: self._dispatch_change_event( object, trait_name, old, new, self.handler ) #------------------------------------------------------------------------------- # 'ExtendedTraitChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class ExtendedTraitChangeNotifyWrapper ( TraitChangeNotifyWrapper ): """ Change notify wrapper for "extended" trait change events.. The "extended notifiers" are set up internally when using extended traits, to add/remove traits listeners when one of the intermediate traits changes. For example, in a listener for the extended trait `a.b`, we need to add/remove listeners to `a:b` when `a` changes. """ def _dispatch_change_event(self, object, trait_name, old, new, handler): """ Prepare and dispatch a trait change event to a listener. """ # Extract the arguments needed from the handler. args = self.argument_transform( object, trait_name, old, new ) # Dispatch the event to the listener. try: self.dispatch( handler, *args ) except Exception: handle_exception( object, trait_name, old, new ) def _notify_method_listener(self, object, trait_name, old, new): """ Dispatch a trait change event to a method listener. """ obj_weak_ref = self.object if obj_weak_ref is not None: # We make sure to hold a reference to the object before invoking # `getattr` so that the listener does not disappear in a # multi-threaded case. obj = obj_weak_ref() if obj is not None: # Dynamically resolve the listener by name. listener = getattr( obj, self.name ) self._dispatch_change_event( object, trait_name, old, new, listener ) def _notify_function_listener(self, object, trait_name, old, new): """ Dispatch a trait change event to a function listener. """ self._dispatch_change_event(object, trait_name, old, new, self.handler) #------------------------------------------------------------------------------- # 'FastUITraitChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class FastUITraitChangeNotifyWrapper ( TraitChangeNotifyWrapper ): """ Dynamic change notify wrapper, dispatching on the UI thread. This class is in charge to dispatch trait change events to dynamic listener, typically created using the `on_trait_change` method and the `dispatch` parameter set to 'ui' or 'fast_ui'. """ def dispatch ( self, handler, *args ): if get_ident() == ui_thread: handler( *args ) else: ui_handler( handler, *args ) #------------------------------------------------------------------------------- # 'NewTraitChangeNotifyWrapper' class: #------------------------------------------------------------------------------- class NewTraitChangeNotifyWrapper ( TraitChangeNotifyWrapper ): """ Dynamic change notify wrapper, dispatching on a new thread. This class is in charge to dispatch trait change events to dynamic listener, typically created using the `on_trait_change` method and the `dispatch` parameter set to 'new'. """ def dispatch ( self, handler, *args ): Thread( target = handler, args = args ).start() traits-4.6.0/traits/trait_numeric.py000066400000000000000000000353371300633736300175730ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 12/13/2004 # #------------------------------------------------------------------------------ """ Trait definitions related to the numpy library. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import warnings from .trait_base import SequenceTypes from .trait_errors import TraitError from .trait_handlers import TraitType, OBJECT_IDENTITY_COMPARE from .trait_types import Str, Any, Int as TInt, Float as TFloat #------------------------------------------------------------------------------- # Deferred imports from numpy: #------------------------------------------------------------------------------- ndarray = None asarray = None #------------------------------------------------------------------------------- # numpy dtype mapping: #------------------------------------------------------------------------------- def dtype2trait ( dtype ): """ Get the corresponding trait for a numpy dtype. """ import numpy if dtype.char in numpy.typecodes['Float']: return TFloat elif dtype.char in numpy.typecodes['AllInteger']: return TInt elif dtype.char[0] == 'S': return Str else: return Any #------------------------------------------------------------------------------- # 'AbstractArray' trait base class: #------------------------------------------------------------------------------- class AbstractArray ( TraitType ): """ Abstract base class for defining numpy-based arrays. """ def __init__ ( self, dtype = None, shape = None, value = None, coerce = False, typecode = None, **metadata ): """ Returns an AbstractArray trait. """ global ndarray, asarray try: import numpy except ImportError: raise TraitError( "Using Array or CArray trait types requires the " "numpy package to be installed." ) from numpy import asarray, ndarray # Mark this as being an 'array' trait: metadata[ 'array' ] = True # Normally use object identity to detect array values changing: metadata.setdefault( 'comparison_mode', OBJECT_IDENTITY_COMPARE ) if typecode is not None: warnings.warn( 'typecode is a deprecated argument; use dtype ' 'instead', DeprecationWarning ) if (dtype is not None) and (dtype != typecode): raise TraitError( 'Inconsistent usage of the dtype and ' 'typecode arguments; use dtype alone.' ) else: dtype = typecode if dtype is not None: try: # Convert the argument into an actual numpy dtype object: dtype = numpy.dtype( dtype ) except TypeError: raise TraitError( 'could not convert %r to a numpy dtype' % dtype ) if shape is not None: if isinstance( shape, SequenceTypes ): for item in shape: if ((item is None) or (type( item ) is int) or (isinstance( item, SequenceTypes ) and (len( item ) == 2) and (type( item[0] ) is int) and (item[0] >= 0) and ((item[1] is None) or ((type( item[1] ) is int) and (item[0] <= item[1]))))): continue raise TraitError, "shape should be a list or tuple" else: raise TraitError, "shape should be a list or tuple" if value is None: value = self._default_for_dtype_and_shape( dtype, shape ) self.dtype = dtype self.shape = shape self.coerce = coerce super( AbstractArray, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that the value is a valid array. """ try: # Make sure the value is an array: type_value = type( value ) if not isinstance( value, ndarray ): if not isinstance( value, SequenceTypes ): self.error( object, name, value ) if self.dtype is not None: value = asarray( value, self.dtype ) else: value = asarray( value ) # Make sure the array is of the right type: if ((self.dtype is not None) and (value.dtype != self.dtype)): if self.coerce: value = value.astype( self.dtype ) else: # XXX: this also coerces. value = asarray( value, self.dtype ) # If no shape requirements, then return the value: trait_shape = self.shape if trait_shape is None: return value # Else make sure that the value's shape is compatible: value_shape = value.shape if len( trait_shape ) == len( value_shape ): for i, dim in enumerate( value_shape ): item = trait_shape[i] if item is not None: if type( item ) is int: if dim != item: break elif ((dim < item[0]) or ((item[1] is not None) and (dim > item[1]))): break else: return value except: pass self.error( object, name, value ) def info ( self ): """ Returns descriptive information about the trait. """ dtype = shape = '' if self.shape is not None: shape = [] for item in self.shape: if item is None: item = '*' elif type( item ) is not int: if item[1] is None: item = '%d..' % item[0] else: item = '%d..%d' % item shape.append( item ) shape = ' with shape %s' % ( tuple( shape ), ) if self.dtype is not None: # FIXME: restore nicer descriptions of dtypes. dtype = ' of %s values' % self.dtype return 'an array%s%s' % ( dtype, shape ) def create_editor ( self ): """ Returns the default UI editor for the trait. """ editor = None auto_set = False if self.auto_set is None: auto_set = True enter_set = self.enter_set or False if self.shape is not None and len( self.shape ) == 2: from traitsui.api import ArrayEditor editor = ArrayEditor( auto_set=auto_set, enter_set=enter_set ) else: from traitsui.api import TupleEditor if self.dtype is None: types = Any else: types = dtype2trait( self.dtype ) editor = TupleEditor( types = types, labels = self.labels or [], cols = self.cols or 1, auto_set = auto_set, enter_set = enter_set ) return editor #-- Private Methods -------------------------------------------------------- def get_default_value ( self ): """ Returns the default value constructor for the type (called from the trait factory. """ return ( 7, ( self.copy_default_value, ( self.validate( None, None, self.default_value ), ), None ) ) def copy_default_value ( self, value ): """ Returns a copy of the default value (called from the C code on first reference to a trait with no current value). """ return value.copy() def _default_for_dtype_and_shape ( self, dtype, shape ): """ Invent a suitable default value for a given dtype and shape. """ from numpy import zeros if dtype is None: # Compatibility with the default of Traits 2.0 dt = int else: dt = dtype if shape is None: value = zeros( ( 0, ), dt ) else: size = [] for item in shape: if item is None: item = 1 elif type( item ) in SequenceTypes: # Given a (minimum-allowed-length, maximum-allowed_length) # pair for a particular axis, use the minimum. item = item[0] size.append( item ) value = zeros( size, dt ) return value #------------------------------------------------------------------------------- # 'Array' trait: #------------------------------------------------------------------------------- class Array ( AbstractArray ): """ Defines a trait whose value must be a numpy array. """ def __init__ ( self, dtype = None, shape = None, value = None, typecode = None, **metadata ): """ Returns an Array trait. Parameters ---------- dtype : a numpy dtype (e.g., int32) The type of elements in the array; if omitted, no type-checking is performed on assigned values. shape : a tuple Describes the required shape of any assigned value. Wildcards and ranges are allowed. The value None within the *shape* tuple means that the corresponding dimension is not checked. (For example, ``shape=(None,3)`` means that the first dimension can be any size, but the second must be 3.) A two-element tuple within the *shape* tuple means that the dimension must be in the specified range. The second element can be None to indicate that there is no upper bound. (For example, ``shape=((3,5),(2,None))`` means that the first dimension must be in the range 3 to 5 (inclusive), and the second dimension must be at least 2.) value : numpy array A default value for the array. Default Value ------------- *value* or ``zeros(min(shape))``, where ``min(shape)`` refers to the minimum shape allowed by the array. If *shape* is not specified, the minimum shape is (0,). Description ----------- An Array trait allows only upcasting of assigned values that are already numpy arrays. It automatically casts tuples and lists of the right shape to the specified *dtype* (just like numpy's **array** does). """ super( Array, self ).__init__( dtype, shape, value, False, typecode = typecode, **metadata ) #------------------------------------------------------------------------------- # 'CArray' trait: #------------------------------------------------------------------------------- class CArray ( AbstractArray ): """ Defines a trait whose value must be a numpy array, with casting allowed. """ def __init__ ( self, dtype = None, shape = None, value = None, typecode = None, **metadata ): """ Returns a CArray trait. Parameters ---------- dtype : a numpy dtype (e.g., int32) The type of elements in the array. shape : a tuple Describes the required shape of any assigned value. Wildcards and ranges are allowed. The value None within the *shape* tuple means that the corresponding dimension is not checked. (For example, ``shape=(None,3)`` means that the first dimension can be any size, but the second must be 3.) A two-element tuple within the *shape* tuple means that the dimension must be in the specified range. The second element can be None to indicate that there is no upper bound. (For example, ``shape=((3,5),(2,None))`` means that the first dimension must be in the range 3 to 5 (inclusive), and the second dimension must be at least 2.) value : numpy array A default value for the array. Default Value ------------- *value* or ``zeros(min(shape))``, where ``min(shape)`` refers to the minimum shape allowed by the array. If *shape* is not specified, the minimum shape is (0,). Description ----------- The trait returned by CArray() is similar to that returned by Array(), except that it allows both upcasting and downcasting of assigned values that are already numpy arrays. It automatically casts tuples and lists of the right shape to the specified *dtype* (just like numpy's **array** does). """ super( CArray, self ).__init__( dtype, shape, value, True, typecode = typecode, **metadata ) #------------------------------------------------------------------------------- # 'ArrayOrNone' trait #------------------------------------------------------------------------------- class ArrayOrNone ( CArray ): """ A trait whose value may be either a NumPy array or None, with casting allowed. The default is None. """ def __init__ ( self, *args, **metadata ): # Normally use object identity to detect array values changing: metadata.setdefault( 'comparison_mode', OBJECT_IDENTITY_COMPARE ) super( ArrayOrNone, self ).__init__( *args, **metadata ) def validate (self, object, name, value ): if value is None: return value return super( ArrayOrNone, self ).validate( object, name, value ) def get_default_value ( self ): dv = self.default_value if dv is None: return ( 0, dv ) else: return ( 7, ( self.copy_default_value, ( self.validate( None, None, dv ), ), None ) ) def _default_for_dtype_and_shape ( self, dtype, shape ): # For ArrayOrNone, if no default is explicitly specified, we # always default to `None`. return None traits-4.6.0/traits/trait_types.py000066400000000000000000003651251300633736300172760ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 03/22/2007 # #------------------------------------------------------------------------------ """ Core Trait definitions. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import datetime import operator import re import sys from os.path import isfile, isdir from types import FunctionType, MethodType, ModuleType from . import trait_handlers from .trait_base import (strx, get_module_name, class_of, SequenceTypes, TypeTypes, ClassTypes, Undefined, TraitsCache, python_version) from .trait_handlers import (TraitType, TraitInstance, TraitListObject, TraitSetObject, TraitSetEvent, TraitDictObject, TraitDictEvent, ThisClass, items_event, RangeTypes, HandleWeakRef) from .traits import (Trait, trait_from, _TraitMaker, _InstanceArgs, code_editor, html_editor, password_editor, shell_editor, date_editor, time_editor) from .trait_errors import TraitError from . import _py2to3 #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- MutableTypes = ( list, dict ) SetTypes = SequenceTypes + ( set, ) #------------------------------------------------------------------------------- # Numeric type fast validator definitions: #------------------------------------------------------------------------------- # A few words about the next block of code: # Validator #11 is a generic validator for possibly coercible types # (see validate_trait_coerce_type in ctraits.c). # # The tuples below are of the form # (11, type1, [type2, type3, ...], [None, ctype1, [ctype2, ...]]) # # 'type1' corresponds to the main type for the trait # 'None' acts as the separator between 'types' and 'ctypes' (coercible types) # # The validation passes if: # 1) The trait value type is (a subtype of) one of 'type1', 'type2', ... # in which case the value is returned as-is # or # 2) The trait value type is (a subtype of) one of 'ctype1', 'ctype2', ... # in which case the value is returned coerced to trait type using # 'return type1(value') try: # The numpy enhanced definitions: from numpy import integer, floating, complexfloating, bool_ int_fast_validate = ( 11, int, integer ) long_fast_validate = ( 11, long, None, int, integer ) float_fast_validate = ( 11, float, floating, None, int, long, integer ) complex_fast_validate = ( 11, complex, complexfloating, None, float, floating, int, integer ) bool_fast_validate = ( 11, bool, None, bool_ ) # Tuple or single type suitable for an isinstance check. _BOOL_TYPES = (bool, bool_) except ImportError: # The standard python definitions (without numpy): int_fast_validate = ( 11, int ) long_fast_validate = ( 11, long, None, int ) float_fast_validate = ( 11, float, None, int, long ) complex_fast_validate = ( 11, complex, None, float, int ) bool_fast_validate = ( 11, bool ) # Tuple or single type suitable for an isinstance check. _BOOL_TYPES = bool #------------------------------------------------------------------------------- # Returns a default text editor: #------------------------------------------------------------------------------- def default_text_editor ( trait, type = None ): auto_set = trait.auto_set if auto_set is None: auto_set = True enter_set = trait.enter_set or False from traitsui.api import TextEditor if type is None: return TextEditor( auto_set = auto_set, enter_set = enter_set ) return TextEditor( auto_set = auto_set, enter_set = enter_set, evaluate = type ) #------------------------------------------------------------------------------- # 'Any' trait: #------------------------------------------------------------------------------- class Any ( TraitType ): """ Defines a trait whose value can be anything. """ #: The default value for the trait: default_value = None #: A description of the type of value this trait accepts: info_text = 'any value' #------------------------------------------------------------------------------- # 'Generic' trait: #------------------------------------------------------------------------------- class Generic ( Any ): """ Defines a trait whose value can be anything and whose definition can be redefined via assignment using a TraitValue object. """ #: The standard metadata for the trait: metadata = { 'trait_value': True } #------------------------------------------------------------------------------- # 'BaseInt' and 'Int' traits: #------------------------------------------------------------------------------- class BaseInt ( TraitType ): """ Defines a trait whose type must be an int or long. """ #: The function to use for evaluating strings to this type: evaluate = int #: The default value for the trait: default_value = 0 #: A description of the type of value this trait accepts: info_text = 'an integer (int or long)' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. """ if type(value) is int: return value elif type(value) is long: return int(value) try: int_value = operator.index( value ) except TypeError: pass else: return int(int_value) self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ return default_text_editor( self, int ) class Int ( BaseInt ): """ Defines a trait whose type must be an int or long using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 20, ) #------------------------------------------------------------------------------- # 'BaseLong' and 'Long' traits: #------------------------------------------------------------------------------- class BaseLong ( TraitType ): """ Defines a trait whose value must be a Python long. """ #: The function to use for evaluating strings to this type: evaluate = long #: The default value for the trait: default_value = 0L #: A description of the type of value this trait accepts: info_text = 'a long' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance( value, long ): return value if isinstance( value, int ): return long( value ) self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ return default_text_editor( self, long ) class Long ( BaseLong ): """ Defines a trait whose value must be a Python long using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = long_fast_validate #------------------------------------------------------------------------------- # 'BaseFloat' and 'Float' traits: #------------------------------------------------------------------------------- class BaseFloat ( TraitType ): """ Defines a trait whose value must be a Python float. """ #: The function to use for evaluating strings to this type: evaluate = float #: The default value for the trait: default_value = 0.0 #: A description of the type of value this trait accepts: info_text = 'a float' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance( value, float ): return value if isinstance( value, ( int, long ) ): return float( value ) self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ return default_text_editor( self, float ) class Float ( BaseFloat ): """ Defines a trait whose value must be a Python float using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = float_fast_validate #------------------------------------------------------------------------------- # 'BaseComplex' and 'Complex' traits: #------------------------------------------------------------------------------- class BaseComplex ( TraitType ): """ Defines a trait whose value must be a Python complex. """ #: The function to use for evaluating strings to this type: evaluate = complex #: The default value for the trait: default_value = 0.0 + 0.0j #: A description of the type of value this trait accepts: info_text = 'a complex number' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance( value, complex ): return value if isinstance( value, ( float, int ) ): return complex( value ) self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ return default_text_editor( self, complex ) class Complex ( BaseComplex ): """ Defines a trait whose value must be a Python complex using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = complex_fast_validate #------------------------------------------------------------------------------- # 'BaseStr' and 'Str' traits: #------------------------------------------------------------------------------- class BaseStr ( TraitType ): """ Defines a trait whose value must be a Python string. """ #: The default value for the trait: default_value = '' #: A description of the type of value this trait accepts: info_text = 'a string' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance( value, basestring ): return value self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ from .traits import multi_line_text_editor auto_set = self.auto_set if auto_set is None: auto_set = True enter_set = self.enter_set or False return multi_line_text_editor(auto_set, enter_set) class Str ( BaseStr ): """ Defines a trait whose value must be a Python string using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 11, basestring ) class Title ( Str ): """ Defines a string type which by default uses the traits ui TitleEditor when used in a View. """ def create_editor ( self ): """ Returns the default traits UI editor to use for a trait. """ from traitsui.api import TitleEditor if hasattr(self, 'allow_selection'): return TitleEditor(allow_selection=self.allow_selection) else: return TitleEditor() #------------------------------------------------------------------------------- # 'BaseUnicode' and 'Unicode' traits: #------------------------------------------------------------------------------- class BaseUnicode ( TraitType ): """ Defines a trait whose value must be a Python unicode string. """ #: The default value for the trait: default_value = u'' #: A description of the type of value this trait accepts: info_text = 'a unicode string' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance( value, unicode ): return value if isinstance( value, str ): return unicode( value ) self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ from .traits import multi_line_text_editor auto_set = self.auto_set if auto_set is None: auto_set = True enter_set = self.enter_set or False return multi_line_text_editor(auto_set, enter_set) class Unicode ( BaseUnicode ): """ Defines a trait whose value must be a Python unicode string using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 11, unicode, None, str ) #------------------------------------------------------------------------------- # 'BaseBytes' and 'Bytes' traits: #------------------------------------------------------------------------------- class BaseBytes(TraitType): """ Defines a trait whose value must be a Python bytes string. """ #: The default value for the trait: default_value = b'' #: A description of the type of value this trait accepts: info_text = 'a bytes string' #: An encoding to use with TraitsUI editors encoding = None def validate(self, object, name, value): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance(value, bytes): return value self.error(object, name, value) def create_editor(self): """ Returns the default traits UI editor for this type of trait. """ from .traits import bytes_editor auto_set = self.auto_set if auto_set is None: auto_set = True enter_set = self.enter_set or False return bytes_editor(auto_set, enter_set, self.encoding) class Bytes(BaseBytes): """ Defines a trait whose value must be a Python bytes string using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = (11, bytes) #------------------------------------------------------------------------------- # 'BaseBool' and 'Bool' traits: #------------------------------------------------------------------------------- class BaseBool ( TraitType ): """ Defines a trait whose value must be a Python boolean. """ #: The function to use for evaluating strings to this type: evaluate = bool #: The default value for the trait: default_value = False #: A description of the type of value this trait accepts: info_text = 'a boolean' def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if isinstance( value, _BOOL_TYPES ): return bool(value) self.error( object, name, value ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ from traitsui.api import BooleanEditor return BooleanEditor() class Bool ( BaseBool ): """ Defines a trait whose value must be a Python boolean using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = bool_fast_validate #------------------------------------------------------------------------------- # 'BaseCInt' and 'CInt' traits: #------------------------------------------------------------------------------- class BaseCInt ( BaseInt ): """ Defines a trait whose value must be a Python int and which supports coercions of non-int values to int. """ #: The function to use for evaluating strings to this type: evaluate = int def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return int( value ) except: self.error( object, name, value ) class CInt ( BaseCInt ): """ Defines a trait whose value must be a Python int and which supports coercions of non-int values to int using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 12, int ) #------------------------------------------------------------------------------- # 'BaseCLong' and 'CLong' traits: #------------------------------------------------------------------------------- class BaseCLong ( BaseLong ): """ Defines a trait whose value must be a Python long and which supports coercions of non-long values to long. """ #: The function to use for evaluating strings to this type: evaluate = long def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return long( value ) except: self.error( object, name, value ) class CLong ( BaseCLong ): """ Defines a trait whose value must be a Python long and which supports coercions of non-long values to long using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 12, long ) #------------------------------------------------------------------------------- # 'BaseCFloat' and 'CFloat' traits: #------------------------------------------------------------------------------- class BaseCFloat ( BaseFloat ): """ Defines a trait whose value must be a Python float and which supports coercions of non-float values to float. """ #: The function to use for evaluating strings to this type: evaluate = float def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return float( value ) except: self.error( object, name, value ) class CFloat ( BaseCFloat ): """ Defines a trait whose value must be a Python float and which supports coercions of non-float values to float using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 12, float ) #------------------------------------------------------------------------------- # 'BaseCComplex' and 'CComplex' traits: #------------------------------------------------------------------------------- class BaseCComplex ( BaseComplex ): """ Defines a trait whose value must be a Python complex and which supports coercions of non-complex values to complex. """ #: The function to use for evaluating strings to this type: evaluate = complex def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return complex( value ) except: self.error( object, name, value ) class CComplex ( BaseCComplex ): """ Defines a trait whose value must be a Python complex and which supports coercions of non-complex values to complex using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 12, complex ) #------------------------------------------------------------------------------- # 'BaseCStr' and 'CStr' traits: #------------------------------------------------------------------------------- class BaseCStr ( BaseStr ): """ Defines a trait whose value must be a Python string and which supports coercions of non-string values to string. """ def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return str( value ) except: try: return unicode( value ) except: self.error( object, name, value ) class CStr ( BaseCStr ): """ Defines a trait whose value must be a Python string and which supports coercions of non-string values to string using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 7, ( ( 12, str ), ( 12, unicode ) ) ) #------------------------------------------------------------------------------- # 'BaseCUnicode' and 'CUnicode' traits: #------------------------------------------------------------------------------- class BaseCUnicode ( BaseUnicode ): """ Defines a trait whose value must be a Python unicode string and which supports coercions of non-unicode values to unicode. """ def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return unicode( value ) except: self.error( object, name, value ) class CUnicode ( BaseCUnicode ): """ Defines a trait whose value must be a Python unicode string and which supports coercions of non-unicode values to unicode using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 12, unicode ) #------------------------------------------------------------------------------- # 'BaseCBytes' and 'CBytes' traits: #------------------------------------------------------------------------------- class BaseCBytes(BaseBytes): """ Defines a trait whose value must be a Python bytes object and which supports coercions of non-bytes values to bytes. """ def validate(self, object, name, value): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return bytes(value) except: self.error(object, name, value) class CBytes(BaseCBytes): """ Defines a trait whose value must be a Python bytes and which supports coercions of non-bytes values bytes using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = (12, bytes) #------------------------------------------------------------------------------- # 'BaseCBool' and 'CBool' traits: #------------------------------------------------------------------------------- class BaseCBool ( BaseBool ): """ Defines a trait whose value must be a Python boolean and which supports coercions of non-boolean values to boolean. """ #: The function to use for evaluating strings to this type: evaluate = bool def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ try: return bool( value ) except: self.error( object, name, value ) class CBool ( BaseCBool ): """ Defines a trait whose value must be a Python boolean and which supports coercions of non-boolean values to boolean using a C-level fast validator. """ #: The C-level fast validator to use: fast_validate = ( 12, bool ) #------------------------------------------------------------------------------- # 'String' trait: #------------------------------------------------------------------------------- class String ( TraitType ): """ Defines a trait whose value must be a Python string whose length is optionally in a specified range, and which optionally matches a specified regular expression. """ def __init__ ( self, value = '', minlen = 0, maxlen = sys.maxint, regex = '', **metadata ): """ Creates a String trait. Parameters ---------- value : str The default value for the string. minlen : integer The minimum length allowed for the string. maxlen : integer The maximum length allowed for the string. regex : str A Python regular expression that the string must match. """ super( String, self ).__init__( value, **metadata ) self.minlen = max( 0, minlen ) self.maxlen = max( self.minlen, maxlen ) self.regex = regex self._init() def _init ( self ): """ Completes initialization of the trait at construction or unpickling time. """ self._validate = 'validate_all' if self.regex != '': self.match = re.compile( self.regex ).match if (self.minlen == 0) and (self.maxlen == sys.maxint): self._validate = 'validate_regex' elif (self.minlen == 0) and (self.maxlen == sys.maxint): self._validate = 'validate_str' else: self._validate = 'validate_len' def validate ( self, object, name, value ): """ Validates that the value is a valid string. """ return getattr( self, self._validate )( object, name, value ) def validate_all ( self, object, name, value ): """ Validates that the value is a valid string in the specified length range which matches the specified regular expression. """ try: value = strx( value ) if ((self.minlen <= len( value ) <= self.maxlen) and (self.match( value ) is not None)): return value except: pass self.error( object, name, value ) def validate_str ( self, object, name, value ): """ Validates that the value is a valid string. """ try: return strx( value ) except: pass self.error( object, name, value ) def validate_len ( self, object, name, value ): """ Validates that the value is a valid string in the specified length range. """ try: value = strx( value ) if self.minlen <= len( value ) <= self.maxlen: return value except: pass self.error( object, name, value ) def validate_regex ( self, object, name, value ): """ Validates that the value is a valid string which matches the specified regular expression. """ try: value = strx( value ) if self.match( value ) is not None: return value except: pass self.error( object, name, value ) def info ( self ): """ Returns a description of the trait. """ msg = '' if (self.minlen != 0) and (self.maxlen != sys.maxint): msg = ' between %d and %d characters long' % ( self.minlen, self.maxlen ) elif self.maxlen != sys.maxint: msg = ' <= %d characters long' % self.maxlen elif self.minlen != 0: msg = ' >= %d characters long' % self.minlen if self.regex != '': if msg != '': msg += ' and' msg += (" matching the pattern '%s'" % self.regex) return 'a string' + msg def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ return default_text_editor( self ) def __getstate__ ( self ): """ Returns the current state of the trait. """ result = self.__dict__.copy() for name in [ 'validate', 'match' ]: if name in result: del result[ name ] return result def __setstate__ ( self, state ): """ Sets the current state of the trait. """ self.__dict__.update( state ) self._init() #------------------------------------------------------------------------------- # 'Regex' trait: #------------------------------------------------------------------------------- class Regex ( String ): """ Defines a trait whose value is a Python string that matches a specified regular expression. """ def __init__ ( self, value = '', regex = '.*', **metadata ): """ Creates a Regex trait. Parameters ---------- value : str The default value of the trait. regex : str The regular expression that the trait value must match. Default Value ------------- *value* or '' """ super( Regex, self ).__init__( value = value, regex = regex, **metadata ) #------------------------------------------------------------------------------- # 'Code' trait: #------------------------------------------------------------------------------- class Code ( String ): """ Defines a trait whose value is a Python string that represents source code in some language. """ #: The standard metadata for the trait: metadata = { 'editor': code_editor } #------------------------------------------------------------------------------- # 'HTML' trait: #------------------------------------------------------------------------------- class HTML ( String ): """ Defines a trait whose value must be a string that is interpreted as being HTML. By default the value is parsed and displayed as HTML in TraitsUI views. The validation of the value does not enforce HTML syntax. """ #: The standard metadata for the trait: metadata = { 'editor': html_editor } #------------------------------------------------------------------------------- # 'Password' trait: #------------------------------------------------------------------------------- class Password ( String ): """ Defines a trait whose value must be a string, optionally of constrained length or matching a regular expression. The trait is identical to a String trait except that by default it uses a PasswordEditor in TraitsUI views, which obscures text entered by the user. """ #: The standard metadata for the trait: metadata = { 'editor': password_editor } #------------------------------------------------------------------------------- # 'Callable' trait: #------------------------------------------------------------------------------- class Callable ( TraitType ): """ Defines a trait whose value must be a Python callable. """ #: The standard metadata for the trait: metadata = { 'copy': 'ref' } #: The default value for the trait: default_value = None #: A description of the type of value this trait accepts: info_text = 'a callable value' def validate ( self, object, name, value ): """ Validates that the value is a Python callable. """ if (value is None) or callable( value ): return value self.error( object, name, value ) #------------------------------------------------------------------------------- # 'BaseType' base class: #------------------------------------------------------------------------------- class BaseType ( TraitType ): """ Defines a trait whose value must be an instance of a simple Python type. """ def validate ( self, object, name, value ): """ Validates that the value is a Python callable. """ if isinstance( value, self.fast_validate[1:] ): return value self.error( object, name, value ) class This ( BaseType ): """ Defines a trait whose value must be an instance of the defining class. """ #: The C-level fast validator to use: fast_validate = ( 2, ) #: A description of the type of value this trait accepts: info_text = 'an instance of the same type as the receiver' def __init__ ( self, value = None, allow_none = True, **metadata ): super( This, self ).__init__( value, **metadata ) if allow_none: self.fast_validate = ( 2, None ) self.validate = self.validate_none self.info = self.info_none def validate ( self, object, name, value ): if isinstance( value, object.__class__ ): return value self.validate_failed( object, name, value ) def validate_none ( self, object, name, value ): if isinstance( value, object.__class__ ) or (value is None): return value self.validate_failed( object, name, value ) def info ( self ): return 'an instance of the same type as the receiver' def info_none ( self ): return 'an instance of the same type as the receiver or None' def validate_failed ( self, object, name, value ): kind = type( value ) if _py2to3.is_InstanceType(kind): msg = 'class %s' % value.__class__.__name__ else: msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) ) self.error( object, name, msg ) class self ( This ): """ Defines a trait whose value must be an instance of the defining class and whose default value is the object containing the trait. """ #: The default value type to use (i.e. 'self'): default_value_type = 2 class Function ( TraitType ): """ Defines a trait whose value must be a Python function. """ #: The C-level fast validator to use: fast_validate = ( 11, FunctionType ) #: A description of the type of value this trait accepts: info_text = 'a function' class Method ( TraitType ): """ Defines a trait whose value must be a Python method. """ #: The C-level fast validator to use: fast_validate = ( 11, MethodType ) #: A description of the type of value this trait accepts: info_text = 'a method' if sys.version_info[0] < 3: from types import ClassType class Class ( TraitType ): """ Defines a trait whose value must be an old-style Python class. """ #: The C-level fast validator to use: fast_validate = ( 11, ClassType ) #: A description of the type of value this trait accepts: info_text = 'an old-style class' class Module ( TraitType ): """ Defines a trait whose value must be a Python module. """ #: The C-level fast validator to use: fast_validate = ( 11, ModuleType ) #: A description of the type of value this trait accepts: info_text = 'a module' #------------------------------------------------------------------------------- # 'Python' trait: #------------------------------------------------------------------------------- class Python ( TraitType ): """ Defines a trait that provides behavior identical to a standard Python attribute. That is, it allows any value to be assigned, and raises an ValueError if an attempt is made to get the value before one has been assigned. It has no default value. This trait is most often used in conjunction with wildcard naming. See the *Traits User Manual* for details on wildcards. """ #: The standard metadata for the trait: metadata = { 'type': 'python' } #: The default value for the trait: default_value = Undefined #------------------------------------------------------------------------------- # 'ReadOnly' trait: #------------------------------------------------------------------------------- class ReadOnly ( TraitType ): """ Defines a trait that is write-once, and then read-only. The initial value of the attribute is the special, singleton object Undefined. The trait allows any value to be assigned to the attribute if the current value is the Undefined object. Once any other value is assigned, no further assignment is allowed. Normally, the initial assignment to the attribute is performed in the class constructor, based on information passed to the constructor. If the read-only value is known in advance of run time, use the Constant() function instead of ReadOnly to define the trait. """ # Defines the CTrait type to use for this trait: ctrait_type = 6 #: The default value for the trait: default_value = Undefined # Create a singleton instance as the trait: ReadOnly = ReadOnly() #------------------------------------------------------------------------------- # 'Disallow' trait: #------------------------------------------------------------------------------- class Disallow ( TraitType ): """ Defines a trait that prevents any value from being assigned or read. That is, any attempt to get or set the value of the trait attribute raises an exception. This trait is most often used in conjunction with wildcard naming, for example, to catch spelling mistakes in attribute names. See the *Traits User Manual* for details on wildcards. """ #: Defines the CTrait type to use for this trait: ctrait_type = 5 # Create a singleton instance as the trait: Disallow = Disallow() #------------------------------------------------------------------------------- # 'Constant' trait: #------------------------------------------------------------------------------- class Constant ( TraitType ): """ Defines a trait whose value is a constant. """ #: Defines the CTrait type to use for this trait: ctrait_type = 7 #: The standard metadata for the trait: metadata = { 'type': 'constant', 'transient': True } def __init__ ( self, value, **metadata ): """ Returns a constant, read-only trait whose value is *value*. Parameters ---------- value : any type except a list or dictionary The default value for the trait. Default Value ------------- *value* Description ----------- Traits of this type are very space efficient (and fast) because *value* is not stored in each instance using the trait, but only in the trait object itself. The *value* cannot be a list or dictionary, because those types have mutable values. """ if type( value ) in MutableTypes: raise TraitError, \ "Cannot define a constant using a mutable list or dictionary" super( Constant, self ).__init__( value, **metadata ) #------------------------------------------------------------------------------- # 'Delegate' trait: #------------------------------------------------------------------------------- class Delegate ( TraitType ): """ Defines a trait whose value is delegated to a trait on another object. """ #: Defines the CTrait type to use for this trait: ctrait_type = 3 #: The standard metadata for the trait: metadata = { 'type': 'delegate', 'transient': False } def __init__ ( self, delegate, prefix = '', modify = False, listenable = True, **metadata ): """ Creates a Delegate trait. """ if prefix == '': prefix_type = 0 elif prefix[-1:] != '*': prefix_type = 1 else: prefix = prefix[:-1] if prefix != '': prefix_type = 2 else: prefix_type = 3 metadata[ '_delegate' ] = delegate metadata[ '_prefix' ] = prefix metadata[ '_listenable' ] = listenable super( Delegate, self ).__init__( **metadata ) self.delegate = delegate self.prefix = prefix self.prefix_type = prefix_type self.modify = modify def as_ctrait ( self ): """ Returns a CTrait corresponding to the trait defined by this class. """ trait = super( Delegate, self ).as_ctrait() trait.delegate( self.delegate, self.prefix, self.prefix_type, self.modify ) return trait #------------------------------------------------------------------------------- # 'DelegatesTo' trait: #------------------------------------------------------------------------------- class DelegatesTo ( Delegate ): """ Defines a trait delegate that matches the standard 'delegate' design pattern. """ def __init__ ( self, delegate, prefix = '', listenable = True, **metadata ): """ Creates a "delegator" trait, whose definition and default value are delegated to a *delegate* trait attribute on another object. Parameters ---------- delegate : str Name of the attribute on the current object which references the object that is the trait's delegate. prefix : str A prefix or substitution applied to the original attribute when looking up the delegated attribute. listenable : bool Indicates whether a listener can be attached to this attribute such that changes to the delagate attribute will trigger it. Description ----------- An object containing a delegator trait attribute must contain a second attribute that references the object containing the delegate trait attribute. The name of this second attribute is passed as the *delegate* argument to the DelegatesTo() function. The following rules govern the application of the prefix parameter: * If *prefix* is empty or omitted, the delegation is to an attribute of the delegate object with the same name as the delegator attribute. * If *prefix* is a valid Python attribute name, then the delegation is to an attribute whose name is the value of *prefix*. * If *prefix* ends with an asterisk ('*') and is longer than one character, then the delegation is to an attribute whose name is the value of *prefix*, minus the trailing asterisk, prepended to the delegator attribute name. * If *prefix* is equal to a single asterisk, the delegation is to an attribute whose name is the value of the delegator object's __prefix__ attribute prepended to delegator attribute name. Note that any changes to the delegator attribute are actually applied to the corresponding attribute on the delegate object. The original object containing the delegator trait is not modified. """ super( DelegatesTo, self ).__init__( delegate, prefix = prefix, modify = True, listenable = listenable, **metadata ) #------------------------------------------------------------------------------- # 'PrototypedFrom' trait: #------------------------------------------------------------------------------- class PrototypedFrom ( Delegate ): """ Defines a trait delegate that matches the standard 'prototype' design pattern. """ def __init__ ( self, prototype, prefix = '', listenable = True, **metadata ): """ Creates a "prototyped" trait, whose definition and default value are obtained from a trait attribute on another object. Parameters ---------- prototype : str Name of the attribute on the current object which references the object that is the trait's prototype. prefix : str A prefix or substitution applied to the original attribute when looking up the prototyped attribute. listenable : bool Indicates whether a listener can be attached to this attribute such that changes to the corresponding attribute on the prototype object will trigger it. Description ----------- An object containing a prototyped trait attribute must contain a second attribute that references the object containing the prototype trait attribute. The name of this second attribute is passed as the *prototype* argument to the PrototypedFrom() function. The following rules govern the application of the prefix parameter: * If *prefix* is empty or omitted, the prototype delegation is to an attribute of the prototype object with the same name as the prototyped attribute. * If *prefix* is a valid Python attribute name, then the prototype delegation is to an attribute whose name is the value of *prefix*. * If *prefix* ends with an asterisk ('*') and is longer than one character, then the prototype delegation is to an attribute whose name is the value of *prefix*, minus the trailing asterisk, prepended to the prototyped attribute name. * If *prefix* is equal to a single asterisk, the prototype delegation is to an attribute whose name is the value of the prototype object's __prefix__ attribute prepended to the prototyped attribute name. Note that any changes to the prototyped attribute are made to the original object, not the prototype object. The prototype object is only used to define to trait type and default value. """ super( PrototypedFrom, self ).__init__( prototype, prefix = prefix, modify = False, listenable = listenable, **metadata ) #------------------------------------------------------------------------------- # 'Expression' class: #------------------------------------------------------------------------------- class Expression ( TraitType ): """ Defines a trait whose value must be a valid Python expression. The compiled form of a valid expression is stored as the mapped value of the trait. """ #: The default value for the trait: default_value = '0' #: A description of the type of value this trait accepts: info_text = 'a valid Python expression' #: Indicate that this is a mapped trait: is_mapped = True def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. """ try: return compile( value, '', 'eval' ) except: self.error( object, name, value ) def post_setattr ( self, object, name, value ): """ Performs additional post-assignment processing. """ object.__dict__[ name + '_' ] = value def mapped_value ( self, value ): """ Returns the 'mapped' value for the specified **value**. """ return compile( value, '', 'eval' ) def as_ctrait ( self ): """ Returns a CTrait corresponding to the trait defined by this class. """ # Tell the C code that 'setattr' should store the original, unadapted # value passed to it: return super( Expression, self ).as_ctrait().setattr_original_value( True ) #------------------------------------------------------------------------------- # 'PythonValue' trait: #------------------------------------------------------------------------------- class PythonValue ( Any ): """ Defines a trait whose value can be of any type, and whose default editor is a Python shell. """ #: The standard metadata for the trait: metadata = { 'editor': shell_editor } #------------------------------------------------------------------------------- # 'BaseFile' and 'File' traits: #------------------------------------------------------------------------------- class BaseFile ( BaseStr ): """ Defines a trait whose value must be the name of a file. """ #: A description of the type of value this trait accepts: info_text = 'a file name' def __init__ ( self, value = '', filter = None, auto_set = False, entries = 0, exists = False, **metadata ): """ Creates a File trait. Parameters ---------- value : str The default value for the trait. filter : str A wildcard string to filter filenames in the file dialog box used by the attribute trait editor. auto_set : bool Indicates whether the file editor updates the trait value after every key stroke. exists : bool Indicates whether the trait value must be an existing file or not. Default Value ------------- *value* or '' """ self.filter = filter self.auto_set = auto_set self.entries = entries self.exists = exists super( BaseFile, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ validated_value = super( BaseFile, self ).validate( object, name, value ) if not self.exists: return validated_value elif isfile( value ): return validated_value self.error( object, name, value ) def create_editor(self): from traitsui.editors.file_editor import FileEditor editor = FileEditor( filter = self.filter or [], auto_set = self.auto_set, entries = self.entries, ) return editor class File ( BaseFile ): """ Defines a trait whose value must be the name of a file using a C-level fast validator. """ def __init__ ( self, value = '', filter = None, auto_set = False, entries = 0, exists = False, **metadata ): """ Creates a File trait. Parameters ---------- value : str The default value for the trait. filter : str A wildcard string to filter filenames in the file dialog box used by the attribute trait editor. auto_set : bool Indicates whether the file editor updates the trait value after every key stroke. exists : bool Indicates whether the trait value must be an existing file or not. Default Value ------------- *value* or '' """ if not exists: # Define the C-level fast validator to use: fast_validate = ( 11, basestring ) super( File, self ).__init__( value, filter, auto_set, entries, exists, **metadata ) #------------------------------------------------------------------------------- # 'BaseDirectory' and 'Directory' traits: #------------------------------------------------------------------------------- class BaseDirectory ( BaseStr ): """ Defines a trait whose value must be the name of a directory. """ #: A description of the type of value this trait accepts: info_text = 'a directory name' def __init__ ( self, value = '', auto_set = False, entries = 0, exists = False, **metadata ): """ Creates a BaseDirectory trait. Parameters ---------- value : str The default value for the trait. auto_set : bool Indicates whether the directory editor updates the trait value after every key stroke. exists : bool Indicates whether the trait value must be an existing directory or not. Default Value ------------- *value* or '' """ self.entries = entries self.auto_set = auto_set self.exists = exists super( BaseDirectory, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that a specified value is valid for this trait. Note: The 'fast validator' version performs this check in C. """ if not self.exists: return super( BaseDirectory, self ).validate( object, name, value ) if isdir( value ): return value self.error( object, name, value ) def create_editor(self): from traitsui.editors.directory_editor import DirectoryEditor editor = DirectoryEditor( auto_set = self.auto_set, entries = self.entries, ) return editor class Directory ( BaseDirectory ): """ Defines a trait whose value must be the name of a directory using a C-level fast validator. """ def __init__ ( self, value = '', auto_set = False, entries = 0, exists = False, **metadata ): """ Creates a Directory trait. Parameters ---------- value : str The default value for the trait. auto_set : bool Indicates whether the directory editor updates the trait value after every key stroke. exists : bool Indicates whether the trait value must be an existing directory or not. Default Value ------------- *value* or '' """ # Define the C-level fast validator to use if the directory existence #: test is not required: if not exists: self.fast_validate = ( 11, basestring ) super( Directory, self ).__init__( value, auto_set, entries, exists, **metadata ) #------------------------------------------------------------------------------- # 'BaseRange' and 'Range' traits: #------------------------------------------------------------------------------- class BaseRange ( TraitType ): """ Defines a trait whose numeric value must be in a specified range. """ def __init__ ( self, low = None, high = None, value = None, exclude_low = False, exclude_high = False, **metadata ): """ Creates a Range trait. Parameters ---------- low : integer, float or string (i.e. extended trait name) The low end of the range. high : integer, float or string (i.e. extended trait name) The high end of the range. value : integer, float or string (i.e. extended trait name) The default value of the trait. exclude_low : bool Indicates whether the low end of the range is exclusive. exclude_high : bool Indicates whether the high end of the range is exclusive. The *low*, *high*, and *value* arguments must be of the same type (integer or float), except in the case where either *low* or *high* is a string (i.e. extended trait name). Default Value ------------- *value*; if *value* is None or omitted, the default value is *low*, unless *low* is None or omitted, in which case the default value is *high*. """ if value is None: if low is not None: value = low else: value = high super( BaseRange, self ).__init__( value, **metadata ) vtype = type( high ) if ((low is not None) and (not issubclass( vtype, ( float, basestring ) ))): vtype = type( low ) is_static = (not issubclass( vtype, basestring )) if is_static and (vtype not in RangeTypes): raise TraitError, ("Range can only be use for int, long or float " "values, but a value of type %s was specified." % vtype) self._low_name = self._high_name = '' self._vtype = Undefined if vtype is float: self._validate = 'float_validate' kind = 4 self._type_desc = 'a floating point number' if low is not None: low = float( low ) if high is not None: high = float( high ) elif vtype is long: self._validate = 'long_validate' self._type_desc = 'a long integer' if low is not None: low = long( low ) if high is not None: high = long( high ) elif vtype is int: self._validate = 'int_validate' kind = 3 self._type_desc = 'an integer' if low is not None: low = int( low ) if high is not None: high = int( high ) else: self.get, self.set, self.validate = self._get, self._set, None self._vtype = None self._type_desc = 'a number' if isinstance( high, basestring ): self._high_name = high = 'object.' + high else: self._vtype = type( high ) high = compile( str( high ), '', 'eval' ) if isinstance( low, basestring ): self._low_name = low = 'object.' + low else: self._vtype = type( low ) low = compile( str( low ), '', 'eval' ) if isinstance( value, basestring ): value = 'object.' + value self._value = compile( str( value ), '', 'eval' ) self.default_value_type = 8 self.default_value = self._get_default_value exclude_mask = 0 if exclude_low: exclude_mask |= 1 if exclude_high: exclude_mask |= 2 if is_static and (vtype is not long): self.init_fast_validator( kind, low, high, exclude_mask ) #: Assign type-corrected arguments to handler attributes: self._low = low self._high = high self._exclude_low = exclude_low self._exclude_high = exclude_high def init_fast_validator ( self, *args ): """ Does nothing for the BaseRange class. Used in the Range class to set up the fast validator. """ pass def validate ( self, object, name, value ): """ Validate that the value is in the specified range. """ return getattr( self, self._validate )( object, name, value ) def float_validate ( self, object, name, value ): """ Validate that the value is a float value in the specified range. """ try: if (isinstance( value, RangeTypes ) and ((self._low is None) or (self._exclude_low and (self._low < value)) or ((not self._exclude_low) and (self._low <= value))) and ((self._high is None) or (self._exclude_high and (self._high > value)) or ((not self._exclude_high) and (self._high >= value)))): return float( value ) except: pass self.error( object, name, value ) def int_validate ( self, object, name, value ): """ Validate that the value is an int value in the specified range. """ try: if (isinstance( value, int_fast_validate[1:]) and ((self._low is None) or (self._exclude_low and (self._low < value)) or ((not self._exclude_low) and (self._low <= value))) and ((self._high is None) or (self._exclude_high and (self._high > value)) or ((not self._exclude_high) and (self._high >= value)))): return value except: pass self.error( object, name, value ) def long_validate ( self, object, name, value ): """ Validate that the value is a long value in the specified range. """ try: valid_types = list(long_fast_validate[1:]) valid_types.remove(None) if (isinstance( value, tuple(valid_types) ) and ((self._low is None) or (self._exclude_low and (self._low < value)) or ((not self._exclude_low) and (self._low <= value))) and ((self._high is None) or (self._exclude_high and (self._high > value)) or ((not self._exclude_high) and (self._high >= value)))): return value except: pass self.error( object, name, value ) def _get_default_value ( self, object ): """ Returns the default value of the range. """ return eval( self._value ) def _get ( self, object, name, trait ): """ Returns the current value of a dynamic range trait. """ cname = '_traits_cache_' + name value = object.__dict__.get( cname, Undefined ) if value is Undefined: object.__dict__[ cname ] = value = eval( self._value ) low = eval( self._low ) high = eval( self._high ) if (low is not None) and (value < low): value = low elif (high is not None) and (value > high): value = high return self._typed_value( value, low, high ) def _set ( self, object, name, value ): """ Sets the current value of a dynamic range trait. """ if not isinstance( value, basestring ): try: low = eval( self._low ) high = eval( self._high ) if (low is None) and (high is None): if isinstance( value, RangeTypes ): self._set_value( object, name, value ) return else: new_value = self._typed_value( value, low, high ) if (((low is None) or (self._exclude_low and (low < new_value)) or ((not self._exclude_low) and (low <= new_value))) and ((high is None) or (self._exclude_high and (high > new_value)) or ((not self._exclude_high) and (high >= new_value)))): self._set_value( object, name, new_value ) return except: pass self.error( object, name, value ) def _typed_value ( self, value, low, high ): """ Returns the specified value with the correct type for the current dynamic range. """ vtype = self._vtype if vtype is None: if low is not None: vtype = type( low ) elif high is not None: vtype = type( high ) else: vtype = lambda x: x return vtype( value ) def _set_value ( self, object, name, value ): """ Sets the specified value as the value of the dynamic range. """ cname = '_traits_cache_' + name old = object.__dict__.get( cname, Undefined ) if old is Undefined: old = eval( self._value ) object.__dict__[ cname ] = value if value != old: object.trait_property_changed( name, old, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ if self._vtype is not Undefined: low = eval( self._low ) high = eval( self._high ) low, high = ( self._typed_value( low, low, high ), self._typed_value( high, low, high ) ) else: low = self._low high = self._high if low is None: if high is None: return self._type_desc return '%s <%s %s' % ( self._type_desc, '='[ self._exclude_high: ], high ) elif high is None: return '%s >%s %s' % ( self._type_desc, '='[ self._exclude_low: ], low ) return '%s <%s %s <%s %s' % ( low, '='[ self._exclude_low: ], self._type_desc, '='[ self._exclude_high: ], high ) def create_editor ( self ): """ Returns the default UI editor for the trait. """ # fixme: Needs to support a dynamic range editor. auto_set = self.auto_set if auto_set is None: auto_set = True from traitsui.api import RangeEditor return RangeEditor( self, mode = self.mode or 'auto', cols = self.cols or 3, auto_set = auto_set, enter_set = self.enter_set or False, low_label = self.low or '', high_label = self.high or '', low_name = self._low_name, high_name = self._high_name ) class Range ( BaseRange ): """ Defines a trait whose numeric value must be in a specified range using a C-level fast validator. """ def init_fast_validator ( self, *args ): """ Set up the C-level fast validator. """ self.fast_validate = args #------------------------------------------------------------------------------- # 'BaseEnum' and 'Enum' traits: #------------------------------------------------------------------------------- class BaseEnum ( TraitType ): """ Defines a trait whose value must be one of a specified set of values. """ def __init__ ( self, *args, **metadata ): """ Returns an Enum trait. Parameters ---------- values : list or tuple The enumeration of all legal values for the trait Default Value ------------- values[0] """ values = metadata.pop( 'values', None ) if isinstance( values, basestring ): n = len( args ) if n == 0: default_value = None elif n == 1: default_value = args[0] else: raise TraitError( "Incorrect number of arguments specified " "when using the 'values' keyword" ) self.name = values self.values = compile( 'object.' + values, '', 'eval' ) self.get, self.set, self.validate = self._get, self._set, None else: default_value = args[0] if (len( args ) == 1) and isinstance( default_value, SequenceTypes): args = default_value default_value = args[0] elif (len( args ) == 2) and isinstance( args[1], SequenceTypes ): args = args[1] self.name = '' self.values = tuple( args ) self.init_fast_validator( 5, self.values ) super( BaseEnum, self ).__init__( default_value, **metadata ) def init_fast_validator ( self, *args ): """ Does nothing for the BaseEnum class. Used in the Enum class to set up the fast validator. """ pass def validate ( self, object, name, value ): """ Validates that the value is one of the enumerated set of valid values. """ if value in self.values: return value self.error( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ if self.name == '': values = self.values else: values = eval( self.values ) return ' or '.join( [ repr( x ) for x in values ] ) def create_editor ( self ): """ Returns the default UI editor for the trait. """ from traitsui.api import EnumEditor values = self if self.name != '': values = None return EnumEditor( values = values, name = self.name, cols = self.cols or 3, evaluate = self.evaluate, mode = self.mode or 'radio' ) def _get ( self, object, name, trait ): """ Returns the current value of a dynamic enum trait. """ value = self.get_value( object, name, trait ) values = eval( self.values ) if value not in values: value = None if len( values ) > 0: value = values[0] return value def _set ( self, object, name, value ): """ Sets the current value of a dynamic range trait. """ if value in eval( self.values ): self.set_value( object, name, value ) else: self.error( object, name, value ) class Enum ( BaseEnum ): """ Defines a trait whose value must be one of a specified set of values using a C-level fast validator. """ def init_fast_validator ( self, *args ): """ Set up the C-level fast validator. """ self.fast_validate = args #------------------------------------------------------------------------------- # 'BaseTuple' and 'Tuple' and 'ValidatedTuple' traits: #------------------------------------------------------------------------------- class BaseTuple ( TraitType ): """ Defines a trait whose value must be a tuple of specified trait types. """ def __init__ ( self, *types, **metadata ): """ Returns a Tuple trait. Parameters ---------- types : zero or more arguments Definition of the default and allowed tuples. If the first item of *types* is a tuple, it is used as the default value. The remaining argument list is used to form a tuple that constrains the values assigned to the returned trait. The trait's value must be a tuple of the same length as the remaining argument list, whose elements must match the types specified by the corresponding items of the remaining argument list. Default Value ------------- 1. If no arguments are specified, the default value is (). 2. If a tuple is specified as the first argument, it is the default value. 3. If a tuple is not specified as the first argument, the default value is a tuple whose length is the length of the argument list, and whose values are the default values for the corresponding trait types. Example for case #2:: mytuple = Tuple(('Fred', 'Betty', 5)) The trait's value must be a 3-element tuple whose first and second elements are strings, and whose third element is an integer. The default value is ``('Fred', 'Betty', 5)``. Example for case #3:: mytuple = Tuple('Fred', 'Betty', 5) The trait's value must be a 3-element tuple whose first and second elements are strings, and whose third element is an integer. The default value is ``('','',0)``. """ if len( types ) == 0: self.init_fast_validator( 11, tuple, None, list ) super( BaseTuple, self ).__init__( (), **metadata ) return default_value = None if isinstance( types[0], tuple ): default_value, types = types[0], types[1:] if len( types ) == 0: types = [ Trait( element ) for element in default_value ] self.types = tuple( [ trait_from( type ) for type in types ] ) self.init_fast_validator( 9, self.types ) if default_value is None: default_value = tuple( [ type.default_value()[1] for type in self.types ] ) super( BaseTuple, self ).__init__( default_value, **metadata ) def init_fast_validator ( self, *args ): """ Saves the validation parameters. """ self.no_type_check = (args[0] == 11) def validate ( self, object, name, value ): """ Validates that the value is a valid tuple. """ if self.no_type_check: if isinstance( value, tuple ): return value if isinstance( value, list ): return tuple( value ) self.error( object, name, value ) try: if isinstance( value, list ): value = tuple( value ) if isinstance( value, tuple ): types = self.types if len( value ) == len( types ): values = [] for i, type in enumerate( types ): values.append( type.validate( object, name, value[i] ) ) return tuple( values ) except: pass self.error( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ if self.no_type_check: return 'a tuple' return 'a tuple of the form: (%s)' % (', '.join( [ type.full_info( object, name, value ) for type in self.types ] )) def create_editor ( self ): """ Returns the default UI editor for the trait. """ from traitsui.api import TupleEditor auto_set = self.auto_set if auto_set is None: auto_set = True enter_set = self.enter_set or False return TupleEditor( types = self.types, labels = self.labels or [], cols = self.cols or 1, auto_set = auto_set, enter_set = enter_set ) class Tuple ( BaseTuple ): """ Defines a trait whose value must be a tuple of specified trait types using a C-level fast validator. """ def init_fast_validator ( self, *args ): """ Set up the C-level fast validator. """ super( Tuple, self ).init_fast_validator( *args ) self.fast_validate = args class ValidatedTuple ( BaseTuple ): """ A Tuple trait that supports custom validation. """ def __init__ ( self, *types, **metadata ): """ Returns a ValidatedTuple trait Parameters ---------- types : zero or more arguments Definition of the default and allowed tuples. (see :class:`~.BaseTuple` for more details) fvalidate : callable, optional A callable to provide the additional custom validation for the tuple. The callable will be passed the tuple value and should return True or False. fvalidate_info : string, optional A string describing the custom validation to use for the error messages. For example:: value_range = ValidatedTuple(Int(0), Int(1), fvalidate=lambda x: x[0] < x[1]) This definition will accept only tuples ``(a, b)`` containing two integers that satisfy ``a < b``. """ metadata.setdefault( 'fvalidate', None ) metadata.setdefault( 'fvalidate_info', '' ) super( ValidatedTuple, self ).__init__( *types, **metadata ) def validate ( self, object, name, value ): """ Validates that the value is a valid tuple. """ values = super( ValidatedTuple, self ).validate( object, name, value ) # Exceptions in the fvalidate function will not result in a TraitError # but will be allowed to propagate up the frame stacks. if self.fvalidate is None or self.fvalidate( values ): return values else: self.error( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ message = 'a tuple of the form: ({0}) that passes custom validation{1}' types_info = ', '.join( [ type_.full_info( object, name, value ) for type_ in self.types ] ) if self.fvalidate_info is not None: fvalidate_info = ': {0}'.format( self.fvalidate_info ) else: fvalidate_info = '' return message.format( types_info, fvalidate_info ) #------------------------------------------------------------------------------- # 'List' trait: #------------------------------------------------------------------------------- class List ( TraitType ): """ Defines a trait whose value must be a list whose items are of the specified trait type. """ info_trait = None default_value_type = 5 _items_event = None def __init__ ( self, trait = None, value = None, minlen = 0, maxlen = sys.maxint, items = True, **metadata ): """ Returns a List trait. Parameters ---------- trait : a trait or value that can be converted to a trait using Trait() The type of item that the list contains. If not specified, the list can contain items of any type. value : list Default value for the list. minlen : integer The minimum length of a list that can be assigned to the trait. maxlen : integer The maximum length of a list that can be assigned to the trait. The length of the list assigned to the trait must be such that:: minlen <= len(list) <= maxlen Default Value ------------- *value* or None """ metadata.setdefault( 'copy', 'deep' ) if isinstance( trait, SequenceTypes ): trait, value = value, list( trait ) if value is None: value = [] self.item_trait = trait_from( trait ) self.minlen = max( 0, minlen ) self.maxlen = max( minlen, maxlen ) self.has_items = items if self.item_trait.instance_handler == '_instance_changed_handler': metadata.setdefault( 'instance_handler', '_list_changed_handler' ) super( List, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that the values is a valid list. .. note:: `object` can be None when validating a default value (see e.g. :meth:`~traits.trait_handlers.TraitType.clone`) """ if (isinstance( value, list ) and (self.minlen <= len( value ) <= self.maxlen)): if object is None: return value return TraitListObject( self, object, name, value ) self.error( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ if self.minlen == 0: if self.maxlen == sys.maxint: size = 'items' else: size = 'at most %d items' % self.maxlen else: if self.maxlen == sys.maxint: size = 'at least %d items' % self.minlen else: size = 'from %s to %s items' % ( self.minlen, self.maxlen ) return 'a list of %s which are %s' % ( size, self.item_trait.full_info( object, name, value ) ) def create_editor ( self ): """ Returns the default UI editor for the trait. """ handler = self.item_trait.handler if isinstance( handler, TraitInstance ) and (self.mode != 'list'): from .api import HasTraits if issubclass( handler.aClass, HasTraits ): from traitsui.api import TableEditor return TableEditor() from traitsui.api import ListEditor return ListEditor( trait_handler = self, rows = self.rows or 5, use_notebook = self.use_notebook is True, page_name = self.page_name or '' ) def inner_traits ( self ): """ Returns the *inner trait* (or traits) for this trait. """ return ( self.item_trait, ) #-- Private Methods -------------------------------------------------------- def items_event ( self ): return items_event() #------------------------------------------------------------------------------- # 'CList' trait: #------------------------------------------------------------------------------- class CList ( List ): """ Defines a trait whose values must be a list whose items are of the specified trait type or which can be coerced to a list whose values are of the specified trait type. """ def validate ( self, object, name, value ): """ Validates that the values is a valid list. """ if not isinstance( value, list ): try: # Should work for all iterables as well as strings (which do # not define an __iter__ method) value = list( value ) except (ValueError, TypeError): value = [ value ] return super( CList, self ).validate( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ return '%s or %s' % ( self.item_trait.full_info( object, name, value), super( CList, self ).full_info( object, name, value ) ) #------------------------------------------------------------------------------- # 'Set' trait: #------------------------------------------------------------------------------- class Set ( TraitType ): """ Defines a trait whose value must be a set whose items are of the specified trait type. """ info_trait = None default_value_type = 9 _items_event = None def __init__ ( self, trait = None, value = None, items = True, **metadata ): """ Returns a Set trait. Parameters ---------- trait : a trait or value that can be converted to a trait using Trait() The type of item that the list contains. If not specified, the list can contain items of any type. value : set Default value for the set. Default Value ------------- *value* or None """ metadata.setdefault( 'copy', 'deep' ) if isinstance( trait, SetTypes ): trait, value = value, set( trait ) if value is None: value = set() self.item_trait = trait_from( trait ) self.has_items = items super( Set, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that the values is a valid set. .. note:: `object` can be None when validating a default value (see e.g. :meth:`~traits.trait_handlers.TraitType.clone`) """ if isinstance( value, set ): if object is None: return value return TraitSetObject( self, object, name, value ) self.error( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ return 'a set of %s' % self.item_trait.full_info( object, name, value ) def create_editor ( self ): """ Returns the default UI editor for the trait. """ # fixme: Needs to be customized for sets. handler = self.item_trait.handler if isinstance( handler, TraitInstance ) and (self.mode != 'list'): from .api import HasTraits if issubclass( handler.aClass, HasTraits ): try: object = handler.aClass() from traitsui.table_column import ObjectColumn from traitsui.table_filter import (EvalFilterTemplate, RuleFilterTemplate, MenuFilterTemplate, EvalTableFilter) from traitsui.api import TableEditor return TableEditor( columns = [ ObjectColumn( name = name ) for name in object.editable_traits() ], filters = [ RuleFilterTemplate, MenuFilterTemplate, EvalFilterTemplate ], edit_view = '', orientation = 'vertical', search = EvalTableFilter(), deletable = True, row_factory = handler.aClass ) except: pass from traitsui.api import ListEditor return ListEditor( trait_handler = self, rows = self.rows or 5, use_notebook = self.use_notebook is True, page_name = self.page_name or '' ) def inner_traits ( self ): """ Returns the *inner trait* (or traits) for this trait. """ return ( self.item_trait, ) #-- Private Methods -------------------------------------------------------- def items_event ( self ): if self.__class__._items_event is None: self.__class__._items_event = \ Event( TraitSetEvent, is_base = False ).as_ctrait() return self.__class__._items_event #------------------------------------------------------------------------------- # 'CSet' trait: #------------------------------------------------------------------------------- class CSet ( Set ): """ Defines a trait whose values must be a set whose items are of the specified trait type or which can be coerced to a set whose values are of the specified trait type. """ def validate ( self, object, name, value ): """ Validates that the values is a valid list. """ if not isinstance( value, set ): try: # Should work for all iterables as well as strings (which do # not define an __iter__ method) value = set( value ) except ( ValueError, TypeError ): value = set( [ value ] ) return super( CSet, self ).validate( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ return '%s or %s' % ( self.item_trait.full_info( object, name, value), super( CSet, self ).full_info( object, name, value ) ) #------------------------------------------------------------------------------- # 'Dict' trait: #------------------------------------------------------------------------------- class Dict ( TraitType ): """ Defines a trait whose value must be a dictionary, optionally with specified types for keys and values. """ info_trait = None default_value_type = 6 _items_event = None def __init__ ( self, key_trait = None, value_trait = None, value = None, items = True, **metadata ): """ Returns a Dict trait. Parameters ---------- key_trait : a trait or value that can convert to a trait using Trait() The trait type for keys in the dictionary; if not specified, any values can be used as keys. value_trait : a trait or value that can convert to a trait using Trait() The trait type for values in the dictionary; if not specified, any values can be used as dictionary values. value : dict The default value for the returned trait. items : bool Indicates whether the value contains items. Default Value ------------- *value* or {} """ if isinstance( key_trait, dict ): key_trait, value_trait, value = value_trait, value, key_trait if value is None: value = {} self.key_trait = trait_from( key_trait ) self.value_trait = trait_from( value_trait ) self.has_items = items handler = self.value_trait.handler if (handler is not None) and handler.has_items: handler = handler.clone() handler.has_items = False self.value_handler = handler super( Dict, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that the value is a valid dictionary. .. note:: `object` can be None when validating a default value (see e.g. :meth:`~traits.trait_handlers.TraitType.clone`) """ if isinstance( value, dict ): if object is None: return value return TraitDictObject( self, object, name, value ) self.error( object, name, value ) def full_info ( self, object, name, value ): """ Returns a description of the trait. """ return ('a dictionary with keys which are %s and with values which ' 'are %s') % ( self.key_trait.full_info( object, name, value ), self.value_trait.full_info( object, name, value ) ) def create_editor ( self ): """ Returns the default UI editor for the trait. """ from traitsui.api import TextEditor return TextEditor( evaluate = eval ) def inner_traits ( self ): """ Returns the *inner trait* (or traits) for this trait. """ return ( self.key_trait, self.value_trait ) #-- Private Methods -------------------------------------------------------- def items_event ( self ): cls = self.__class__ if cls._items_event is None: cls._items_event = \ Event( TraitDictEvent, is_base = False ).as_ctrait() return cls._items_event #------------------------------------------------------------------------------- # 'BaseInstance' and 'Instance' traits: #------------------------------------------------------------------------------- # Allowed values and mappings for the 'adapt' keyword: AdaptMap = { 'no': 0, 'yes': 1, 'default': 2 } class BaseClass ( TraitType ): """ Base class for types which have an associated class which can be determined dynamically by specifying a string name for the class (e.g. 'package1.package2.module.class'. Any subclass must define instances with 'klass' and 'module' attributes that contain the string name of the class (or actual class object) and the module name that contained the original trait definition (used for resolving local class names (e.g. 'LocalClass')). This is an abstract class that only provides helper methods used to resolve the class name into an actual class object. """ def resolve_class ( self, object, name, value ): klass = self.validate_class( self.find_class( self.klass ) ) if klass is None: self.validate_failed( object, name, value ) self.klass = klass def validate_class ( self, klass ): return klass def find_class ( self, klass ): module = self.module col = klass.rfind( '.' ) if col >= 0: module = klass[ : col ] klass = klass[ col + 1: ] theClass = getattr( sys.modules.get( module ), klass, None ) if (theClass is None) and (col >= 0): try: mod = __import__( module ) for component in module.split( '.' )[1:]: mod = getattr( mod, component ) theClass = getattr( mod, klass, None ) except: pass return theClass def validate_failed ( self, object, name, value ): self.error( object, name, value ) def validate_implements ( value, klass, unused = None ): """ Checks to see if a specified value implements the instance class interface (if it is an interface). """ from .has_traits import isinterface from .interface_checker import check_implements return isinterface(klass) and check_implements( value.__class__, klass ) #: Tell the C-base code about the 'validate_implements' function (used by the #: 'fast_validate' code for Instance types): from . import ctraits ctraits._validate_implements( validate_implements ) class BaseInstance ( BaseClass ): """ Defines a trait whose value must be an instance of a specified class, or one of its subclasses. """ adapt_default = 'no' def __init__ ( self, klass = None, factory = None, args = None, kw = None, allow_none = True, adapt = None, module = None, **metadata ): """ Returns an Instance trait. Parameters ---------- klass : class or instance The object that forms the basis for the trait; if it is an instance, then trait values must be instances of the same class or a subclass. This object is not the default value, even if it is an instance. factory : callable A callable, typically a class, that when called with *args* and *kw*, returns the default value for the trait. If not specified, or *None*, *klass* is used as the factory. args : tuple Positional arguments for generating the default value. kw : dictionary Keyword arguments for generating the default value. allow_none : bool Indicates whether None is allowed as a value. adapt : str A string specifying how adaptation should be applied. The possible values are: - 'no': Adaptation is not allowed. - 'yes': Adaptation is allowed. If adaptation fails, an exception should be raised. - 'default': Adaptation is allowed. If adaptation fails, the default value for the trait should be used. Default Value ------------- **None** if *klass* is an instance or if it is a class and *args* and *kw* are not specified. Otherwise, the default value is the instance obtained by calling ``klass(*args, **kw)``. Note that the constructor call is performed each time a default value is assigned, so each default value assigned is a unique instance. """ if klass is None: raise TraitError( 'A %s trait must have a class specified.' % self.__class__.__name__ ) metadata.setdefault( 'copy', 'deep' ) metadata.setdefault( 'instance_handler', '_instance_changed_handler' ) adapt = adapt or self.adapt_default if adapt not in AdaptMap: raise TraitError( "'adapt' must be 'yes', 'no' or 'default'." ) if isinstance( factory, tuple ): if args is None: args, factory = factory, klass elif isinstance( args, dict ): factory, args, kw = klass, factory, args elif (kw is None) and isinstance( factory, dict ): kw, factory = factory, klass elif ((args is not None) or (kw is not None)) and (factory is None): factory = klass self._allow_none = allow_none self.adapt = AdaptMap[ adapt ] self.module = module or get_module_name() if isinstance( klass, basestring ): self.klass = klass else: if not isinstance( klass, ClassTypes ): klass = klass.__class__ self.klass = klass self.init_fast_validate() value = factory if factory is not None: if args is None: args = () if kw is None: if isinstance( args, dict ): kw = args args = () else: kw = {} elif not isinstance( kw, dict ): raise TraitError( "The 'kw' argument must be a dictionary." ) if ((not callable( factory )) and (not isinstance( factory, basestring ))): if (len( args ) > 0) or (len( kw ) > 0): raise TraitError( "'factory' must be callable" ) else: value = _InstanceArgs( factory, args, kw ) self.default_value = value super( BaseInstance, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that the value is a valid object instance. """ from traits.adaptation.api import adapt if value is None: if self._allow_none: return value self.validate_failed( object, name, value ) if isinstance( self.klass, basestring ): self.resolve_class( object, name, value ) if self.adapt == 0: try: if value is adapt( value, self.klass ): return value except: if validate_implements( value, self.klass ): return value elif self.adapt == 1: try: return adapt( value, self.klass ) except: if validate_implements( value, self.klass ): return value else: result = adapt( value, self.klass, None ) if result is None: if validate_implements( value, self.klass ): return value result = self.default_value if isinstance( result, _InstanceArgs ): result = result[0]( *result[1], **result[2] ) return result self.validate_failed( object, name, value ) def info ( self ): """ Returns a description of the trait. """ klass = self.klass if not isinstance( klass, basestring ): klass = klass.__name__ if self.adapt == 0: result = class_of( klass ) else: result = ('an implementor of, or can be adapted to implement, %s' % klass) if self._allow_none: return result + ' or None' return result def get_default_value ( self ): """ Returns a tuple of the form: ( default_value_type, default_value ) which describes the default value for this trait. """ dv = self.default_value dvt = self.default_value_type if dvt < 0: if not isinstance( dv, _InstanceArgs ): return super( BaseInstance, self ).get_default_value() self.default_value_type = dvt = 7 self.default_value = dv = ( self.create_default_value, dv.args, dv.kw ) return ( dvt, dv ) def create_editor ( self ): """ Returns the default traits UI editor for this type of trait. """ from traitsui.api import InstanceEditor return InstanceEditor( label = self.label or '', view = self.view or '', kind = self.kind or 'live' ) #-- Private Methods -------------------------------------------------------- def create_default_value ( self, *args, **kw ): klass = args[0] if isinstance( klass, basestring ): klass = self.validate_class( self.find_class( klass ) ) if klass is None: raise TraitError, 'Unable to locate class: ' + args[0] return klass( *args[1:], **kw ) #: fixme: Do we still need this method using the new style?... def allow_none ( self ): self._allow_none = True self.init_fast_validate() def init_fast_validate ( self ): """ Does nothing for the BaseInstance' class. Used by the 'Instance', 'AdaptedTo' and 'AdaptsTo' classes to set up the C-level fast validator. """ pass def resolve_class ( self, object, name, value ): super( BaseInstance, self ).resolve_class( object, name, value ) #: fixme: The following is quite ugly, because it wants to try and fix #: the trait referencing this handler to use the 'fast path' now that the #: actual class has been resolved. The problem is finding the trait, # especially in the case of List(Instance('foo')), where the # object.base_trait(...) value is the List trait, not the Instance # trait, so we need to check for this and pull out the List # 'item_trait'. Obviously this does not extend well to other traits # containing nested trait references (Dict?)... self.init_fast_validate() trait = object.base_trait( name ) handler = trait.handler if handler is not self: set_validate = getattr( handler, 'set_validate', None ) if set_validate is not None: # The outer trait is a TraitCompound. Recompute its # fast_validate table now that we have updated ours. # FIXME: there are probably still issues if the TraitCompound is # further nested. set_validate() else: item_trait = getattr( handler, 'item_trait', None ) if item_trait is not None and item_trait.handler is self: # The outer trait is a List trait. trait = item_trait handler = self else: return if handler.fast_validate is not None: trait.set_validate( handler.fast_validate ) class Instance ( BaseInstance ): """ Defines a trait whose value must be an instance of a specified class, or one of its subclasses using a C-level fast validator. """ def init_fast_validate ( self ): """ Sets up the C-level fast validator. """ from .has_traits import isinterface if (self.adapt == 0) and (not isinterface(self.klass)): fast_validate = [ 1, self.klass ] if self._allow_none: fast_validate = [ 1, None, self.klass ] if self.klass in TypeTypes: fast_validate[0] = 0 self.fast_validate = tuple( fast_validate ) else: self.fast_validate = ( 19, self.klass, self.adapt, self._allow_none ) class Supports( Instance ): """ A traits whose value must support a specified protocol. In other words, the value of the trait directly provide, or can be adapted to, the given protocol (Interface or type). The value of the trait after assignment is the possibly adapted value (i.e., it is the original assigned value if that provides the protocol, or is an adapter otherwise). The original, unadapted value is stored in a "shadow" attribute with the same name followed by an underscore (e.g., ``foo`` and ``foo_``). """ adapt_default = 'yes' def post_setattr ( self, object, name, value ): """ Performs additional post-assignment processing. """ # Save the original, unadapted value in the mapped trait: object.__dict__[ name + '_' ] = value def as_ctrait ( self ): """ Returns a CTrait corresponding to the trait defined by this class. """ return self.modify_ctrait( super( AdaptedTo, self ).as_ctrait() ) def modify_ctrait ( self, ctrait ): # Tell the C code that the 'post_setattr' method wants the original, # unadapted value passed to 'setattr': return ctrait.post_setattr_original_value( True ) # Alias defined for backward compatibility with Traits 4.3.0 AdaptedTo = Supports class AdaptsTo ( Supports ): """ A traits whose value must support a specified protocol. In other words, the value of the trait directly provide, or can be adapted to, the given protocol (Interface or type). The value of the trait after assignment is the original, unadapted value. A possibly adapted value is stored in a "shadow" attribute with the same name followed by an underscore (e.g., ``foo`` and ``foo_``). """ def modify_ctrait ( self, ctrait ): # Tell the C code that 'setattr' should store the original, unadapted # value passed to it: return ctrait.setattr_original_value( True ) #------------------------------------------------------------------------------- # 'Type' trait: #------------------------------------------------------------------------------- class Type ( BaseClass ): """ Defines a trait whose value must be a subclass of a specified class. """ def __init__ ( self, value = None, klass = None, allow_none = True, **metadata ): """ Returns an Type trait. Parameters ---------- value : class or None klass : class or None allow_none : bool Indicates whether None is allowed as an assignable value. Even if **False**, the default *value* may be **None**. Default Value ------------- **None** if *klass* is an instance or if it is a class and *args* and *kw* are not specified. Otherwise, the default value is the instance obtained by calling ``klass(*args, **kw)``. Note that the constructor call is performed each time a default value is assigned, so each default value assigned is a unique instance. """ if value is None: if klass is None: klass = object elif klass is None: klass = value if isinstance( klass, basestring ): self.validate = self.resolve elif not isinstance( klass, ClassTypes ): raise TraitError( "A Type trait must specify a class." ) self.klass = klass self._allow_none = allow_none self.module = get_module_name() super( Type, self ).__init__( value, **metadata ) def validate ( self, object, name, value ): """ Validates that the value is a valid object instance. """ try: if issubclass( value, self.klass ): return value except: if (value is None) and (self._allow_none): return value self.error( object, name, value ) def resolve ( self, object, name, value ): """ Resolves a class originally specified as a string into an actual class, then resets the trait so that future calls will be handled by the normal validate method. """ if isinstance( self.klass, basestring ): self.resolve_class( object, name, value ) del self.validate return self.validate( object, name, value ) def info ( self ): """ Returns a description of the trait. """ klass = self.klass if not isinstance( klass, basestring ): klass = klass.__name__ result = 'a subclass of ' + klass if self._allow_none: return result + ' or None' return result def get_default_value ( self ): """ Returns a tuple of the form: ( default_value_type, default_value ) which describes the default value for this trait. """ if not isinstance( self.default_value, basestring ): return super( Type, self ).get_default_value() return ( 7, ( self.resolve_default_value, (), None ) ) def resolve_default_value ( self ): """ Resolves a class name into a class so that it can be used to return the class as the default value of the trait. """ if isinstance( self.klass, basestring ): try: self.resolve_class( None, None, None ) del self.validate except: raise TraitError( 'Could not resolve %s into a valid class' % self.klass ) return self.klass #------------------------------------------------------------------------------- # 'Event' trait: #------------------------------------------------------------------------------- class Event ( TraitType ): def __init__ ( self, trait = None, **metadata ): metadata[ 'type' ] = 'event' metadata[ 'transient' ] = True super( Event, self ).__init__( **metadata ) self.trait = None if trait is not None: self.trait = trait_from( trait ) validate = self.trait.get_validate() if validate is not None: self.fast_validate = validate def full_info ( self, object, name, value ): """ Returns a description of the trait. """ trait = self.trait if trait is None: return 'any value' return trait.full_info( object, name, value ) # Handle circular module dependencies: trait_handlers.Event = Event #------------------------------------------------------------------------------- # 'Button' trait: #------------------------------------------------------------------------------- class Button ( Event ): """ Defines a trait whose UI editor is a button. """ def __init__ ( self, label = '', image = None, values_trait = None, style = 'button', orientation = 'vertical', width_padding = 7, height_padding = 5, view = None, **metadata ): """ Returns a trait event whose editor is a button. Parameters ---------- label : str The label for the button. image : pyface.ImageResource An image to display on the button. style : one of: 'button', 'radio', 'toolbar', 'checkbox' The style of button to display. values_trait : str For a "button" or "toolbar" style, the name of an enum trait whose values will populate a drop-down menu on the button. The selected value will replace the label on the button. orientation : one of: 'horizontal', 'vertical' The orientation of the label relative to the image. width_padding : integer between 0 and 31 Extra padding (in pixels) added to the left and right sides of the button. height_padding : integer between 0 and 31 Extra padding (in pixels) added to the top and bottom of the button. Default Value ------------- No default value because events do not store values. """ self.label = label self.values_trait = values_trait self.image = image self.style = style self.orientation = orientation self.width_padding = width_padding self.height_padding = height_padding self.view = view super( Button, self ).__init__( **metadata ) def create_editor(self): from traitsui.api import ButtonEditor editor = ButtonEditor( label = self.label, values_trait = self.values_trait, image = self.image, style = self.style, orientation = self.orientation, width_padding = self.width_padding, height_padding = self.height_padding, view = self.view, ) return editor #------------------------------------------------------------------------------- # 'ToolbarButton' trait: #------------------------------------------------------------------------------- class ToolbarButton ( Button ): """ Defines a trait whose UI editor is a button that can be used on a toolbar. """ def __init__ ( self, label = '', image = None, style = 'toolbar', orientation = 'vertical', width_padding = 2, height_padding = 2, **metadata ): """ Returns a trait event whose editor is a toolbar button. Parameters ---------- label : str The label for the button image : pyface.ImageResource An image to display on the button style : one of: 'button', 'radio', 'toolbar', 'checkbox' The style of button to display orientation : one of ['horizontal', 'vertical'] The orientation of the label relative to the image width_padding : integer between 0 and 31 Extra padding (in pixels) added to the left and right sides of the button height_padding : integer between 0 and 31 Extra padding (in pixels) added to the top and bottom of the button Default Value ------------- No default value because events do not store values. """ super( ToolbarButton, self ).__init__( label, image=image, style=style, orientation=orientation, width_padding=width_padding, height_padding=height_padding, **metadata ) #------------------------------------------------------------------------------- # 'Either' trait: #------------------------------------------------------------------------------- class Either ( TraitType ): """ Defines a trait whose value can be any of of a specified list of traits. """ def __init__ ( self, *traits, **metadata ): """ Creates a trait whose value can be any of of a specified list of traits. """ self.trait_maker = _TraitMaker( metadata.pop( 'default', None ), *traits, **metadata ) def as_ctrait ( self ): """ Returns a CTrait corresponding to the trait defined by this class. """ return self.trait_maker.as_ctrait() #------------------------------------------------------------------------------- # 'Symbol' trait: #------------------------------------------------------------------------------- class Symbol ( TraitType ): #: A description of the type of value this trait accepts: info_text = ("an object or a string of the form " "'[package.package...package.]module[:symbol[([arg1,...,argn])]]' " "specifying where to locate the object") def get ( self, object, name ): value = object.__dict__.get( name, Undefined ) if value is Undefined: cache = TraitsCache + name ref = object.__dict__.get( cache ) if ref is None: object.__dict__[ cache ] = ref = \ object.trait( name ).default_value_for( object, name ) if isinstance( ref, basestring ): object.__dict__[ name ] = value = self._resolve( ref ) return value def set ( self, object, name, value ): dict = object.__dict__ old = dict.get( name, Undefined ) if isinstance( value, basestring ): dict.pop( name, None ) dict[ TraitsCache + name ] = value object.trait_property_changed( name, old ) else: dict[ name ] = value object.trait_property_changed( name, old, value ) def _resolve ( self, ref ): try: path = ref.split( ':', 1 ) module = __import__( path[0] ) for component in path[0].split( '.' )[1:]: module = getattr( module, component ) if len( path ) == 1: return module elements = path[1].split( '(', 1 ) symbol = getattr( module, elements[0] ) if len( elements ) == 1: return symbol args = eval( '(' + elements[1] ) if not isinstance( args, tuple ): args = ( args, ) return symbol( *args ) except: raise TraitError( "Could not resolve '%s' into a valid symbol." % ref ) if python_version >= 2.5: import uuid #--------------------------------------------------------------------------- # 'UUID' trait: #--------------------------------------------------------------------------- class UUID ( TraitType ): """ Defines a trait whose value is a globally unique UUID (type 4). """ #: A description of the type of value this trait accepts: info_text = 'a read-only UUID' def __init__ ( self, **metadata ): """ Returns a UUID trait. """ super( UUID, self ).__init__( None, **metadata ) def validate ( self, object, name, value ): """ Raises an error, since no values can be assigned to the trait. """ raise TraitError( "The '%s' trait of %s instance is a read-only " "UUID." % ( name, class_of( object ) ) ) def get_default_value ( self ): return ( 7, ( self._create_uuid, (), None ) ) #-- Private Methods --------------------------------------------------- def _create_uuid ( self ): return uuid.uuid4() #------------------------------------------------------------------------------- # 'WeakRef' trait: #------------------------------------------------------------------------------- class WeakRef ( Instance ): """ Returns a trait whose value must be an instance of the same type (or a subclass) of the specified *klass*, which can be a class or an instance. Note that the trait only maintains a weak reference to the assigned value. """ def __init__ ( self, klass = 'traits.has_traits.HasTraits', allow_none = False, adapt = 'yes', **metadata ): """ Returns a WeakRef trait. Only a weak reference is maintained to any object assigned to a WeakRef trait. If no other references exist to the assigned value, the value may be garbage collected, in which case the value of the trait becomes None. In all other cases, the value returned by the trait is the original object. Parameters ---------- klass : class or instance The object that forms the basis for the trait. If *klass* is omitted, then values must be an instance of HasTraits. allow_none : boolean Indicates whether None can be assigned. Default Value ------------- **None** (even if allow_none==False) """ metadata.setdefault( 'copy', 'ref' ) super( WeakRef, self ).__init__( klass, allow_none = allow_none, adapt = adapt, module = get_module_name(), **metadata ) def get ( self, object, name ): value = getattr( object, name + '_', None ) if value is not None: return value.value() return None def set ( self, object, name, value ): old = self.get( object, name ) if value is None: object.__dict__[ name + '_' ] = None else: object.__dict__[ name + '_' ] = HandleWeakRef( object, name, value ) if value is not old: object.trait_property_changed( name, old, value ) def resolve_class ( self, object, name, value ): # fixme: We have to override this method to prevent the 'fast validate' # from being set up, since the trait using this is a 'property' style # trait which is not currently compatible with the 'fast_validate' # style (causes internal Python SystemError messages). klass = self.find_class( self.klass ) if klass is None: self.validate_failed( object, name, value ) self.klass = klass #-- Date Trait definition ---------------------------------------------------- Date = BaseInstance(datetime.date, editor=date_editor) #-- Time Trait definition ---------------------------------------------------- Time = BaseInstance(datetime.time, editor=time_editor) #------------------------------------------------------------------------------- # Create predefined, reusable trait instances: #------------------------------------------------------------------------------- # Synonym for Bool; default value is False. false = Bool # Boolean values only; default value is True. true = Bool( True ) # Allows any value to be assigned; no type-checking is performed. # Default value is Undefined. undefined = Any( Undefined ) #-- List Traits ---------------------------------------------------------------- #: List of integer values; default value is []. ListInt = List( int ) #: List of float values; default value is []. ListFloat = List( float ) #: List of string values; default value is []. ListStr = List( str ) #: List of Unicode string values; default value is []. ListUnicode = List( unicode ) #: List of complex values; default value is []. ListComplex = List( complex ) #: List of Boolean values; default value is []. ListBool = List( bool ) #: List of function values; default value is []. ListFunction = List( FunctionType ) #: List of method values; default value is []. ListMethod = List( MethodType ) if sys.version_info[0] < 3: from types import ClassType, InstanceType #: List of class values; default value is []. ListClass = List( ClassType ) #: List of instance values; default value is []. ListInstance = List( InstanceType ) #: List of container type values; default value is []. ListThis = List( ThisClass ) #-- Dictionary Traits ---------------------------------------------------------- #: Only a dictionary of string:Any values can be assigned; only string keys can #: be inserted. The default value is {}. DictStrAny = Dict( str, Any ) #: Only a dictionary of string:string values can be assigned; only string keys #: with string values can be inserted. The default value is {}. DictStrStr = Dict( str, str ) #: Only a dictionary of string:integer values can be assigned; only string keys #: with integer values can be inserted. The default value is {}. DictStrInt = Dict( str, int ) #: Only a dictionary of string:long-integer values can be assigned; only string #: keys with long-integer values can be inserted. The default value is {}. DictStrLong = Dict( str, long ) #: Only a dictionary of string:float values can be assigned; only string keys #: with float values can be inserted. The default value is {}. DictStrFloat = Dict( str, float ) #: Only a dictionary of string:bool values can be assigned; only string keys #: with boolean values can be inserted. The default value is {}. DictStrBool = Dict( str, bool ) #: Only a dictionary of string:list values can be assigned; only string keys #: with list values can be assigned. The default value is {}. DictStrList = Dict( str, list ) traits-4.6.0/traits/trait_value.py000066400000000000000000000174271300633736300172450ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2008, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Original Date: 04/01/2008 # #------------------------------------------------------------------------------ """ Defines the TraitValue class, used for creating special, dynamic trait values. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import from .trait_base import Undefined from .traits import CTrait from .has_traits import HasTraits, HasPrivateTraits from .trait_errors import TraitError from .trait_types import Tuple, Dict, Any, Str, Instance, Event, Callable from .trait_handlers import TraitType, _read_only, _write_only, _arg_count #------------------------------------------------------------------------------- # 'BaseTraitValue' class: #------------------------------------------------------------------------------- class BaseTraitValue ( HasPrivateTraits ): # Subclasses can define this trait as a property: # value = Property #-- Public Methods --------------------------------------------------------- def as_ctrait ( self, original_trait ): """ Returns the low-level C-based trait for this TraitValue. """ notifiers = original_trait._notifiers( 0 ) if self._ctrait is not None: if (notifiers is None) or (len( notifiers ) == 0): return self._ctrait trait = CTrait( 0 ) trait.clone( self._ctrait ) else: trait = self._as_ctrait( original_trait ) if ((trait is not None) and (notifiers is not None) and (len( notifiers ) > 0)): trait._notifiers( 1 ).extend( notifiers ) return trait #-- Private Methods -------------------------------------------------------- def _as_ctrait ( self, original_trait ): """ Returns the low-level C-based trait for this TraitValue. """ value_trait = self.trait( 'value' ) if value_trait is None: return None if value_trait.type != 'property': raise TraitError( "Invalid TraitValue specified." ) metadata = { 'type': 'property', '_trait_value': self } getter, setter, validate = value_trait.property() read_only = (getter is _read_only) if not read_only: getter = self._getter metadata[ 'transient' ] = True if setter is not _write_only: if read_only: setter = self._read_only_setter else: setter = self._setter metadata[ 'transient' ] = True return self._property_trait( getter, setter, validate, metadata ) def _property_trait ( self, getter, setter, validate, metadata ): """ Returns a properly constructed 'property' trait. """ n = 0 if validate is not None: n = _arg_count( validate ) trait = CTrait( 4 ) trait.property( getter, _arg_count( getter ), setter, _arg_count( setter ), validate, n ) trait.value_allowed( True ) trait.value_property( True ) trait.__dict__ = metadata return trait def _getter ( self, object, name ): return self.value def _setter ( self, object, name, value ): old_value = self.value self.value = value new_value = self.value if new_value != old_value: object.trait_property_changed( name, old_value, new_value ) def _read_only_setter ( self, object, name, value ): self.value = value object.trait_property_changed( name, Undefined, value ) #------------------------------------------------------------------------------- # 'TraitValue' class: #------------------------------------------------------------------------------- class TraitValue ( BaseTraitValue ): #: The callable used to define a default value: default = Callable #: The positional arguments to pass to the callable default value: args = Tuple #: The keyword arguments to pass to the callable default value: kw = Dict #: The trait to use as the new trait type: type = Any #: The object to delegate the new value to: delegate = Instance( HasTraits ) #: The name of the trait on the delegate object to get the new value from: name = Str #-- Private Methods -------------------------------------------------------- def _as_ctrait ( self, original_trait ): """ Returns the low-level C-based trait for this TraitValue. """ if self.default is not None: trait = CTrait( 0 ) trait.clone( original_trait ) if original_trait.__dict__ is not None: trait.__dict__ = original_trait.__dict__.copy() trait.default_value( 7, ( self.default, self.args, self.kw ) ) elif self.type is not None: type = self.type try: rc = issubclass( type, TraitType ) except: rc = False if rc: type = type( *self.args, **self.kw ) if not isinstance( type, TraitType ): raise TraitError( ("The 'type' attribute of a TraitValue " "instance must be a TraitType instance or subclass, but a " "value of %s was specified.") % self.trait ) self._ctrait = trait = type.as_ctrait() trait.value_allowed( True ) elif self.delegate is None: return None else: if self.name == '': raise TraitError( "You must specify a non-empty string " "value for the 'name' attribute when using the " "'delegate' trait of a TraitValue instance." ) metadata = { 'type': 'property', '_trait_value': self, 'transient': True } getter = self._delegate_getter setter = self._delegate_setter validate = None self.add_trait( 'value', Event() ) self.delegate.on_trait_change( self._delegate_modified, self.name ) trait = self._property_trait( getter, setter, validate, metadata ) return trait def _delegate_getter ( self, object, name ): return getattr( self.delegate, self.name ) def _delegate_setter ( self, object, name, value ): setattr( self.delegate, self.name, value ) #-- Traits Event Handlers -------------------------------------------------- def _delegate_modified ( self ): self.value = True #-- Helper Function Definitions ------------------------------------------------ def SyncValue ( delegate, name ): return TraitValue( delegate = delegate, name = name ) def TypeValue ( type ): return TraitValue( type = type ) def DefaultValue ( default, args = (), kw = {} ): return TraitValue( default = default, args = args, kw = kw ) #------------------------------------------------------------------------------- # Tell the C-based traits module about the 'BaseTraitValue' class: #------------------------------------------------------------------------------- from . import ctraits ctraits._value_class( BaseTraitValue ) traits-4.6.0/traits/traits.py000066400000000000000000001367501300633736300162350ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Original Date: 06/21/2002 # # Rewritten as a C-based type extension: 06/21/2004 # #------------------------------------------------------------------------------ """ Defines the 'core' traits for the Traits package. A trait is a type definition that can be used for normal Python object attributes, giving the attributes some additional characteristics: Initialization: Traits have predefined values that do not need to be explicitly initialized in the class constructor or elsewhere. Validation: Trait attributes have flexible, type-checked values. Delegation: Trait attributes' values can be delegated to other objects. Notification: Trait attributes can automatically notify interested parties when their values change. Visualization: Trait attributes can automatically construct (automatic or programmer-defined) user interfaces that allow their values to be edited or displayed) .. note:: 'trait' is a synonym for 'property', but is used instead of the word 'property' to differentiate it from the Python language 'property' feature. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import sys from types import FunctionType, MethodType NoneType = type(None) # Python 3's types does not include NoneType from . import trait_handlers from .ctraits import cTrait from .trait_errors import TraitError from .trait_base import (SequenceTypes, Self, Undefined, Missing, TypeTypes, add_article) from .trait_handlers import (TraitHandler, TraitInstance, TraitFunction, TraitCoerceType, TraitCastType, TraitEnum, TraitCompound, TraitMap, TraitString, ThisClass, TraitType, _arg_count, _read_only, _write_only, _undefined_get, _undefined_set) #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- # Mapping from 'ctrait' default value types to a string representation: KindMap = { 0: 'value', 1: 'value', 2: 'self', 3: 'list', 4: 'dict', 5: 'list', 6: 'dict', 7: 'factory', 8: 'method' } #------------------------------------------------------------------------------- # Editor factory functions: #------------------------------------------------------------------------------- PasswordEditor = None MultilineTextEditor = None BytesEditors = {} SourceCodeEditor = None HTMLTextEditor = None PythonShellEditor = None DateEditor = None TimeEditor = None def password_editor ( auto_set=True, enter_set=False ): """ Factory function that returns an editor for passwords. """ global PasswordEditor if PasswordEditor is None: from traitsui.api import TextEditor PasswordEditor = TextEditor( password = True , auto_set = auto_set, enter_set = enter_set ) return PasswordEditor def multi_line_text_editor ( auto_set=True, enter_set=False ): """ Factory function that returns a text editor for multi-line strings. """ global MultilineTextEditor if MultilineTextEditor is None: from traitsui.api import TextEditor MultilineTextEditor = TextEditor( multi_line = True, auto_set = auto_set, enter_set = enter_set ) return MultilineTextEditor def bytes_editor(auto_set=True, enter_set=False, encoding=None): """ Factory function that returns a text editor for bytes. """ global BytesEditors if encoding is not None: if isinstance(encoding, str): import codecs encoding = codecs.lookup(encoding) if (auto_set, enter_set, encoding) not in BytesEditors: from traitsui.api import TextEditor if encoding is None: # py3-compatible bytes <-> hex unicode string format = lambda b: b.encode('hex').decode('ascii') evaluate = lambda s: s.encode('ascii').decode('hex') else: format = encoding.decode evaluate = encoding.encode BytesEditors[(auto_set, enter_set, encoding)] = TextEditor( multi_line=True, format_func=format, evaluate=evaluate, auto_set=auto_set, enter_set=enter_set ) return BytesEditors[(auto_set, enter_set, encoding)] def code_editor ( ): """ Factory function that returns an editor that treats a multi-line string as source code. """ global SourceCodeEditor if SourceCodeEditor is None: from traitsui.api import CodeEditor SourceCodeEditor = CodeEditor() return SourceCodeEditor def html_editor ( ): """ Factory function for an "editor" that displays a multi-line string as interpreted HTML. """ global HTMLTextEditor if HTMLTextEditor is None: from traitsui.api import HTMLEditor HTMLTextEditor = HTMLEditor() return HTMLTextEditor def shell_editor ( ): """ Factory function that returns a Python shell for editing Python values. """ global PythonShellEditor if PythonShellEditor is None: from traitsui.api import ShellEditor PythonShellEditor = ShellEditor() return PythonShellEditor def time_editor ( ): """ Factory function that returns a Time editor for editing Time values. """ global TimeEditor if TimeEditor is None: from traitsui.api import TimeEditor TimeEditor = TimeEditor() return TimeEditor def date_editor ( ): """ Factory function that returns a Date editor for editing Date values. """ global DateEditor if DateEditor is None: from traitsui.api import DateEditor DateEditor = DateEditor() return DateEditor #------------------------------------------------------------------------------- # 'CTrait' class (extends the underlying cTrait c-based type): #------------------------------------------------------------------------------- class CTrait ( cTrait ): """ Extends the underlying C-based cTrait type. """ #--------------------------------------------------------------------------- # Allows a derivative trait to be defined from this one: #--------------------------------------------------------------------------- def __call__ ( self, *args, **metadata ): handler = self.handler if isinstance( handler, TraitType ): dict = (self.__dict__ or {}).copy() dict.update( metadata ) return handler( *args, **dict ) metadata.setdefault( 'parent', self ) return Trait( *(args + ( self, )), **metadata ) #--------------------------------------------------------------------------- # (Python) property definitions: #--------------------------------------------------------------------------- def __get_default ( self ): kind, value = self.default_value() if kind in ( 2, 7, 8 ): return Undefined if kind in ( 4, 6 ): return value.copy() if kind in ( 3, 5 ): return value[:] return value default = property( __get_default ) def __get_default_kind ( self ): return KindMap[ self.default_value()[0] ] default_kind = property( __get_default_kind ) def __get_trait_type ( self ): handler = self.handler if handler is not None: return handler else: from .trait_types import Any return Any trait_type = property( __get_trait_type ) def __get_inner_traits ( self ): handler = self.handler if handler is not None: return handler.inner_traits() return () inner_traits = property( __get_inner_traits ) #--------------------------------------------------------------------------- # Returns whether or not this trait is of a specified trait type: #--------------------------------------------------------------------------- def is_trait_type ( self, trait_type ): """ Returns whether or not this trait is of a specified trait type. """ return isinstance( self.trait_type, trait_type ) #--------------------------------------------------------------------------- # Returns the user interface editor associated with the trait: #--------------------------------------------------------------------------- def get_editor ( self ): """ Returns the user interface editor associated with the trait. """ from traitsui.api import EditorFactory # See if we have an editor: editor = self.editor if editor is None: # Else see if the trait handler has an editor: handler = self.handler if handler is not None: editor = handler.get_editor( self ) # If not, give up and use a default text editor: if editor is None: from traitsui.api import TextEditor editor = TextEditor # If the result is not an EditorFactory: if not isinstance( editor, EditorFactory ): # Then it should be a factory for creating them: args = () traits = {} if type( editor ) in SequenceTypes: for item in editor[:]: if type( item ) in SequenceTypes: args = tuple( item ) elif isinstance( item, dict ): traits = item if traits.get( 'trait', 0 ) is None: traits = traits.copy() traits[ 'trait' ] = self else: editor = item editor = editor( *args, **traits ) # Cache the result: self.editor = editor # Return the resulting EditorFactory object: return editor #--------------------------------------------------------------------------- # Returns the help text for a trait: #--------------------------------------------------------------------------- def get_help ( self, full = True ): """ Returns the help text for a trait. Parameters ---------- full : bool Indicates whether to return the value of the *help* attribute of the trait itself. Description ----------- If *full* is False or the trait does not have a **help** string, the returned string is constructed from the **desc** attribute on the trait and the **info** string on the trait's handler. """ if full: help = self.help if help is not None: return help handler = self.handler if handler is not None: info = 'must be %s.' % handler.info() else: info = 'may be any value.' desc = self.desc if self.desc is None: return info.capitalize() return 'Specifies %s and %s' % ( desc, info ) #--------------------------------------------------------------------------- # Returns a description of the trait: #--------------------------------------------------------------------------- def full_info ( self, object, name, value ): """ Returns a description of the trait. """ handler = self.handler if handler is not None: return handler.full_info( object, name, value ) return 'any value' #--------------------------------------------------------------------------- # Returns a description of the trait: #--------------------------------------------------------------------------- def info ( self ): """ Returns a description of the trait. """ handler = self.handler if handler is not None: return handler.info() return 'any value' #--------------------------------------------------------------------------- # Returns the pickleable form of a CTrait object: #--------------------------------------------------------------------------- def __reduce_ex__ ( self, protocol ): return ( __newobj__, ( self.__class__, 0 ), self.__getstate__() ) #--------------------------------------------------------------------------- # Registers listeners on an assigned 'TraitValue' object's 'value' # property: #--------------------------------------------------------------------------- def _register ( self, object, name ): """ Registers listeners on an assigned 'TraitValue' object's 'value' property. """ def handler ( ): object.trait_property_changed( name, None ) tv = self._trait_value handlers = tv._handlers if handlers is None: tv._handlers = handlers = {} handlers[ ( id( object ), name ) ] = handler tv.on_trait_change( handler, 'value' ) #--------------------------------------------------------------------------- # Unregisters listeners on an assigned 'TraitValue' object's 'value' # property: #--------------------------------------------------------------------------- def _unregister ( self, object, name ): """ Unregisters listeners on an assigned 'TraitValue' object's 'value' property. """ tv = self._trait_value handlers = tv._handlers key = ( id( object ), name ) handler = handlers.get( key ) if handler is not None: del handlers[ key ] tv.on_trait_change( handler, 'value', remove = True ) # Make sure the Python-level version of the trait class is known to all # interested parties: from . import ctraits ctraits._ctrait( CTrait ) #------------------------------------------------------------------------------- # Constants: #------------------------------------------------------------------------------- ConstantTypes = ( NoneType, int, long, float, complex, str, unicode ) PythonTypes = ( str, unicode, int, long, float, complex, list, tuple, dict, FunctionType, MethodType, type, NoneType ) if sys.version_info[0] < 3: from types import InstanceType,ClassType PythonTypes = PythonTypes[:-2] + (InstanceType,ClassType) + PythonTypes[2:] CallableTypes = ( FunctionType, MethodType ) TraitTypes = ( TraitHandler, CTrait ) DefaultValues = { str: '', unicode: u'', int: 0, long: 0L, float: 0.0, complex: 0j, list: [], tuple: (), dict: {}, bool: False } DefaultValueSpecial = [ Missing, Self ] DefaultValueTypes = [ list, dict ] #------------------------------------------------------------------------------- # Function used to unpickle new-style objects: #------------------------------------------------------------------------------- def __newobj__ ( cls, *args ): """ Unpickles new-style objects. """ return cls.__new__( cls, *args ) #------------------------------------------------------------------------------- # Returns the type of default value specified: #------------------------------------------------------------------------------- def _default_value_type ( default_value ): try: return DefaultValueSpecial.index( default_value ) + 1 except: try: return DefaultValueTypes.index( type( default_value ) ) + 3 except: return 0 #------------------------------------------------------------------------------- # 'TraitFactory' class: #------------------------------------------------------------------------------- class TraitFactory ( object ): ### Need a docstring here. #--------------------------------------------------------------------------- # Initializes the object: #--------------------------------------------------------------------------- def __init__ ( self, maker_function = None ): if maker_function is not None: self.maker_function = maker_function self.__doc__ = maker_function.__doc__ #--------------------------------------------------------------------------- # Creates a CTrait instance: #--------------------------------------------------------------------------- def __call__ ( self, *args, **metadata ): return self.maker_function( *args, **metadata ) class TraitImportError ( TraitFactory ): """ Defines a factory class for deferring import problems until encountering code that actually tries to use the unimportable trait. """ #--------------------------------------------------------------------------- # Initializes the object: #--------------------------------------------------------------------------- def __init__ ( self, message ): self.message = message #--------------------------------------------------------------------------- # Creates a CTrait instance: #--------------------------------------------------------------------------- def __call__ ( self, *args, **metadata ): raise TraitError( self.message ) #------------------------------------------------------------------------------- # Returns a trait created from a TraitFactory instance: #------------------------------------------------------------------------------- _trait_factory_instances = {} def trait_factory ( trait ): global _trait_factory_instances tid = id( trait ) if tid not in _trait_factory_instances: _trait_factory_instances[ tid ] = trait() return _trait_factory_instances[ tid ] #------------------------------------------------------------------------------- # Casts a CTrait or TraitFactory to a CTrait but returns None if it is neither: #------------------------------------------------------------------------------- def trait_cast ( something ): """ Casts a CTrait, TraitFactory or TraitType to a CTrait but returns None if it is none of those. """ if isinstance( something, CTrait ): return something if isinstance( something, TraitFactory ): return trait_factory( something ) if isinstance( something, type ) and issubclass( something, TraitType ): return something().as_ctrait() if isinstance( something, TraitType ): return something.as_ctrait() return None #------------------------------------------------------------------------------- # Attempts to cast a value to a trait. Returns either a trait or the original # value: #------------------------------------------------------------------------------- def try_trait_cast ( something ): """ Attempts to cast a value to a trait. Returns either a trait or the original value. """ return trait_cast( something ) or something #------------------------------------------------------------------------------- # Returns a trait derived from its input: #------------------------------------------------------------------------------- def trait_from ( something ): """ Returns a trait derived from its input. """ from .trait_types import Any if isinstance( something, CTrait ): return something if something is None: something = Any if isinstance( something, TraitFactory ): return trait_factory( something ) if isinstance( something, type ) and issubclass( something, TraitType ): return something().as_ctrait() if isinstance( something, TraitType ): return something.as_ctrait() return Trait( something ) # Patch the reference to 'trait_from' in 'trait_handlers.py': trait_handlers.trait_from = trait_from #--- 'instance' traits --------------------------------------------------------- class _InstanceArgs ( object ): def __init__ ( self, factory, args, kw ): self.args = ( factory, ) + args self.kw = kw #--- 'creates a run-time default value' ---------------------------------------- class Default ( object ): """ Generates a value the first time it is accessed. A Default object can be used anywhere a default trait value would normally be specified, to generate a default value dynamically. """ def __init__ ( self, func = None, args = (), kw = None ): self.default_value = ( func, args, kw ) #------------------------------------------------------------------------------- # Factory function for creating C-based traits: #------------------------------------------------------------------------------- def Trait ( *value_type, **metadata ): """ Creates a trait definition. Parameters ---------- This function accepts a variety of forms of parameter lists: +-------------------+---------------+-------------------------------------+ | Format | Example | Description | +===================+===============+=====================================+ | Trait(*default*) | Trait(150.0) | The type of the trait is inferred | | | | from the type of the default value, | | | | which must be in *ConstantTypes*. | +-------------------+---------------+-------------------------------------+ | Trait(*default*, | Trait(None, | The trait accepts any of the | | *other1*, | 0, 1, 2, | enumerated values, with the first | | *other2*, ...) | 'many') | value being the default value. The | | | | values must be of types in | | | | *ConstantTypes*, but they need not | | | | be of the same type. The *default* | | | | value is not valid for assignment | | | | unless it is repeated later in the | | | | list. | +-------------------+---------------+-------------------------------------+ | Trait([*default*, | Trait([None, | Similar to the previous format, but | | *other1*, | 0, 1, 2, | takes an explicit list or a list | | *other2*, ...]) | 'many']) | variable. | +-------------------+---------------+-------------------------------------+ | Trait(*type*) | Trait(Int) | The *type* parameter must be a name | | | | of a Python type (see | | | | *PythonTypes*). Assigned values | | | | must be of exactly the specified | | | | type; no casting or coercion is | | | | performed. The default value is the | | | | appropriate form of zero, False, | | | | or emtpy string, set or sequence. | +-------------------+---------------+-------------------------------------+ | Trait(*class*) |:: | Values must be instances of *class* | | | | or of a subclass of *class*. The | | | class MyClass:| default value is None, but None | | | pass | cannot be assigned as a value. | | | foo = Trait( | | | | MyClass) | | +-------------------+---------------+-------------------------------------+ | Trait(None, |:: | Similar to the previous format, but | | *class*) | | None *can* be assigned as a value. | | | class MyClass:| | | | pass | | | | foo = Trait( | | | | None, MyClass)| | +-------------------+---------------+-------------------------------------+ | Trait(*instance*) |:: | Values must be instances of the | | | | same class as *instance*, or of a | | | class MyClass:| subclass of that class. The | | | pass | specified instance is the default | | | i = MyClass() | value. | | | foo = | | | | Trait(i) | | +-------------------+---------------+-------------------------------------+ | Trait(*handler*) | Trait( | Assignment to this trait is | | | TraitEnum ) | validated by an object derived from | | | | **traits.TraitHandler**. | +-------------------+---------------+-------------------------------------+ | Trait(*default*, | Trait(0.0, 0.0| This is the most general form of | | { *type* | | 'stuff', | the function. The notation: | | *constant* | | TupleType) | ``{...|...|...}+`` means a list of | | *dict* | *class* || | one or more of any of the items | | *function* | | | listed between the braces. Thus, the| | *handler* | | | most general form of the function | | *trait* }+ ) | | consists of a default value, | | | | followed by one or more of several | | | | possible items. A trait defined by | | | | multiple items is called a | | | | "compound" trait. | +-------------------+---------------+-------------------------------------+ All forms of the Trait function accept both predefined and arbitrary keyword arguments. The value of each keyword argument becomes bound to the resulting trait object as the value of an attribute having the same name as the keyword. This feature lets you associate metadata with a trait. The following predefined keywords are accepted: Keywords -------- desc : str Describes the intended meaning of the trait. It is used in exception messages and fly-over help in user interfaces. label : str Provides a human-readable name for the trait. It is used to label user interface editors for traits. editor : traits.api.Editor Instance of a subclass Editor object to use when creating a user interface editor for the trait. See the "Traits UI User Guide" for more information on trait editors. comparison_mode : int Indicates when trait change notifications should be generated based upon the result of comparing the old and new values of a trait assignment: * 0 (NO_COMPARE): The values are not compared and a trait change notification is generated on each assignment. * 1 (OBJECT_IDENTITY_COMPARE): A trait change notification is generated if the old and new values are not the same object. * 2 (RICH_COMPARE): A trait change notification is generated if the old and new values are not equal using Python's 'rich comparison' operator. This is the default. rich_compare : bool Indicates whether the basis for considering a trait attribute value to have changed is a "rich" comparison (True, the default), or simple object identity (False). This attribute can be useful in cases where a detailed comparison of two objects is very expensive, or where you do not care whether the details of an object change, as long as the same object is used. .. deprecated:: 3.0.3 Use ``comparison_mode`` instead """ return _TraitMaker( *value_type, **metadata ).as_ctrait() # Handle circular module dependencies: trait_handlers.Trait = Trait #------------------------------------------------------------------------------- # '_TraitMaker' class: #------------------------------------------------------------------------------- class _TraitMaker ( object ): # Ctrait type map for special trait types: type_map = { 'event': 2, 'constant': 7 } #--------------------------------------------------------------------------- # Initialize the object: #--------------------------------------------------------------------------- def __init__ ( self, *value_type, **metadata ): metadata.setdefault( 'type', 'trait' ) self.define( *value_type, **metadata ) #--------------------------------------------------------------------------- # Define the trait: #--------------------------------------------------------------------------- def define ( self, *value_type, **metadata ): default_value_type = -1 default_value = handler = clone = None if len( value_type ) > 0: default_value = value_type[0] value_type = value_type[1:] if ((len( value_type ) == 0) and (type( default_value ) in SequenceTypes)): default_value, value_type = default_value[0], default_value if len( value_type ) == 0: default_value = try_trait_cast( default_value ) if default_value in PythonTypes: handler = TraitCoerceType( default_value ) default_value = DefaultValues.get( default_value ) elif isinstance( default_value, CTrait ): clone = default_value default_value_type, default_value = clone.default_value() metadata[ 'type' ] = clone.type elif isinstance( default_value, TraitHandler ): handler = default_value default_value = None elif default_value is ThisClass: handler = ThisClass() default_value = None else: typeValue = type( default_value ) if isinstance(default_value, basestring): string_options = self.extract( metadata, 'min_len', 'max_len', 'regex' ) if len( string_options ) == 0: handler = TraitCastType( typeValue ) else: handler = TraitString( **string_options ) elif typeValue in TypeTypes: handler = TraitCastType( typeValue ) else: metadata.setdefault( 'instance_handler', '_instance_changed_handler' ) handler = TraitInstance( default_value ) if default_value is handler.aClass: default_value = DefaultValues.get( default_value ) else: enum = [] other = [] map = {} self.do_list( value_type, enum, map, other ) if (((len( enum ) == 1) and (enum[0] is None)) and ((len( other ) == 1) and isinstance( other[0], TraitInstance ))): enum = [] other[0].allow_none() metadata.setdefault( 'instance_handler', '_instance_changed_handler' ) if len( enum ) > 0: if (((len( map ) + len( other )) == 0) and (default_value not in enum)): enum.insert( 0, default_value ) other.append( TraitEnum( enum ) ) if len( map ) > 0: other.append( TraitMap( map ) ) if len( other ) == 0: handler = TraitHandler() elif len( other ) == 1: handler = other[0] if isinstance( handler, CTrait ): clone, handler = handler, None metadata[ 'type' ] = clone.type elif isinstance( handler, TraitInstance ): metadata.setdefault( 'instance_handler', '_instance_changed_handler' ) if default_value is None: handler.allow_none() elif isinstance( default_value, _InstanceArgs ): default_value_type = 7 default_value = ( handler.create_default_value, default_value.args, default_value.kw ) elif (len( enum ) == 0) and (len( map ) == 0): aClass = handler.aClass typeValue = type( default_value ) if typeValue is dict: default_value_type = 7 default_value = ( aClass, (), default_value ) elif not isinstance( default_value, aClass ): if typeValue is not tuple: default_value = ( default_value, ) default_value_type = 7 default_value = ( aClass, default_value, None ) else: for i, item in enumerate( other ): if isinstance( item, CTrait ): if item.type != 'trait': raise TraitError, ("Cannot create a complex " "trait containing %s trait." % add_article( item.type ) ) handler = item.handler if handler is None: break other[i] = handler else: handler = TraitCompound( other ) # Save the results: self.handler = handler self.clone = clone if default_value_type < 0: if isinstance( default_value, Default ): default_value_type = 7 default_value = default_value.default_value else: if (handler is None) and (clone is not None): handler = clone.handler if handler is not None: default_value_type = handler.default_value_type if default_value_type < 0: try: default_value = handler.validate( None, '', default_value ) except: pass if default_value_type < 0: default_value_type = _default_value_type( default_value ) self.default_value_type = default_value_type self.default_value = default_value self.metadata = metadata.copy() #--------------------------------------------------------------------------- # Determine the correct TraitHandler for each item in a list: #--------------------------------------------------------------------------- def do_list ( self, list, enum, map, other ): for item in list: if item in PythonTypes: other.append( TraitCoerceType( item ) ) else: item = try_trait_cast( item ) typeItem = type( item ) if typeItem in ConstantTypes: enum.append( item ) elif typeItem in SequenceTypes: self.do_list( item, enum, map, other ) elif typeItem is dict: map.update( item ) elif typeItem in CallableTypes: other.append( TraitFunction( item ) ) elif item is ThisClass: other.append( ThisClass() ) elif isinstance( item, TraitTypes ): other.append( item ) else: other.append( TraitInstance( item ) ) #--------------------------------------------------------------------------- # Returns a properly initialized 'CTrait' instance: #--------------------------------------------------------------------------- def as_ctrait ( self ): metadata = self.metadata trait = CTrait( self.type_map.get( metadata.get( 'type' ), 0 ) ) clone = self.clone if clone is not None: trait.clone( clone ) if clone.__dict__ is not None: trait.__dict__ = clone.__dict__.copy() trait.default_value( self.default_value_type, self.default_value ) handler = self.handler if handler is not None: trait.handler = handler validate = getattr( handler, 'fast_validate', None ) if validate is None: validate = handler.validate trait.set_validate( validate ) post_setattr = getattr( handler, 'post_setattr', None ) if post_setattr is not None: trait.post_setattr = post_setattr trait.is_mapped( handler.is_mapped ) # Note: The use of 'rich_compare' metadata is deprecated; use # 'comparison_mode' metadata instead: rich_compare = metadata.get( 'rich_compare' ) if rich_compare is not None: trait.rich_comparison( rich_compare is True ) comparison_mode = metadata.get( 'comparison_mode' ) if comparison_mode is not None: trait.comparison_mode( comparison_mode ) trait.value_allowed( metadata.get( 'trait_value', False ) is True ) if len( metadata ) > 0: if trait.__dict__ is None: trait.__dict__ = metadata else: trait.__dict__.update( metadata ) return trait #--------------------------------------------------------------------------- # Extract a set of keywords from a dictionary: #--------------------------------------------------------------------------- def extract ( self, from_dict, *keys ): to_dict = {} for key in keys: if key in from_dict: to_dict[ key ] = from_dict[ key ] del from_dict[ key ] return to_dict #------------------------------------------------------------------------------- # Factory function for creating C-based trait properties: #------------------------------------------------------------------------------- def Property ( fget = None, fset = None, fvalidate = None, force = False, handler = None, trait = None, **metadata ): """ Returns a trait whose value is a Python property. Parameters ---------- fget : function The "getter" function for the property. fset : function The "setter" function for the property. fvalidate : function The validation function for the property. The method should return the value to set or raise TraitError if the new value is not valid. force : bool Indicates whether to use only the function definitions specified by **fget** and **fset**, and not look elsewhere on the class. handler : function A trait handler function for the trait. trait : Trait or value A trait definition or a value that can be converted to a trait that constrains the values of the property trait. Description ----------- If no getter, setter or validate functions are specified (and **force** is not True), it is assumed that they are defined elsewhere on the class whose attribute this trait is assigned to. For example:: class Bar(HasTraits): # A float traits Property that should be always positive. foo = Property(Float) # Shadow trait attribute _foo = Float def _set_foo(self,x): self._foo = x def _validate_foo(self, x): if x <= 0: raise TraitError( 'foo property should be a positive number') return x def _get_foo(self): return self._foo You can use the **depends_on** metadata attribute to indicate that the property depends on the value of another trait. The value of **depends_on** is an extended name specifier for traits that the property depends on. The property will a trait change notification if any of the traits specified by **depends_on** change. For example:: class Wheel ( Part ): axle = Instanced( Axle ) position = Property( depends_on = 'axle.chassis.position' ) For details of the extended trait name syntax, refer to the on_trait_change() method of the HasTraits class. """ metadata[ 'type' ] = 'property' # If no parameters specified, must be a forward reference (if not forced): if (not force) and (fset is None): sum = ((fget is not None) + (fvalidate is not None) + (trait is not None)) if sum <= 1: if sum == 0: return ForwardProperty( metadata ) handler = None if fget is not None: trait = fget if trait is not None: trait = trait_cast( trait ) if trait is not None: fvalidate = handler = trait.handler if fvalidate is not None: fvalidate = handler.validate if (fvalidate is not None) or (trait is not None): if 'editor' not in metadata: if (trait is not None) and (trait.editor is not None): metadata[ 'editor' ] = trait.editor return ForwardProperty( metadata, fvalidate, handler ) if fget is None: metadata[ 'transient' ] = True if fset is None: fget = _undefined_get fset = _undefined_set else: fget = _write_only elif fset is None: fset = _read_only metadata[ 'transient' ] = True if trait is not None: trait = trait_cast( trait ) handler = trait.handler if (fvalidate is None) and (handler is not None): fvalidate = handler.validate if ('editor' not in metadata) and (trait.editor is not None): metadata[ 'editor' ] = trait.editor metadata.setdefault( 'depends_on', getattr( fget, 'depends_on', None ) ) if ((metadata.get( 'depends_on' ) is not None) and getattr( fget, 'cached_property', False )): metadata.setdefault( 'cached', True ) n = 0 trait = CTrait( 4 ) trait.__dict__ = metadata.copy() if fvalidate is not None: n = _arg_count( fvalidate ) trait.property( fget, _arg_count( fget ), fset, _arg_count( fset ), fvalidate, n ) trait.handler = handler return trait Property = TraitFactory( Property ) class ForwardProperty ( object ): """ Used to implement Property traits where accessor functions are defined implicitly on the class. """ def __init__ ( self, metadata, validate = None, handler = None ): self.metadata = metadata.copy() self.validate = validate self.handler = handler #------------------------------------------------------------------------------- # Dictionary used to handle return type mapping special cases: #------------------------------------------------------------------------------- SpecialNames = { ### 'int': trait_factory( Int ), ### 'long': trait_factory( Long ), ### 'float': trait_factory( Float ), ### 'complex': trait_factory( Complex ), ### 'str': trait_factory( Str ), ### 'unicode': trait_factory( Unicode ), ### 'bool': trait_factory( Bool ), ### 'list': trait_factory( List ), ### 'tuple': trait_factory( Tuple ), ### 'dict': trait_factory( Dict ) } #-- Date Trait definition ---------------------------------------------------- #Date = Instance(datetime.date, metadata = { 'editor': date_editor }) #-- Time Trait definition ---------------------------------------------------- #Time = Instance(datetime.time, metadata = { 'editor': time_editor }) #------------------------------------------------------------------------------- # Create predefined, reusable trait instances: #------------------------------------------------------------------------------- # Generic trait with 'object' behavior: generic_trait = CTrait( 8 ) #------------------------------------------------------------------------------- # User interface related color and font traits: #------------------------------------------------------------------------------- def Color ( *args, **metadata ): """ Returns a trait whose value must be a GUI toolkit-specific color. Description ----------- For wxPython, the returned trait accepts any of the following values: * A wx.Colour instance * A wx.ColourPtr instance * an integer whose hexadecimal form is 0x*RRGGBB*, where *RR* is the red value, *GG* is the green value, and *BB* is the blue value Default Value ------------- For wxPython, 0x000000 (that is, white) """ from traitsui.toolkit_traits import ColorTrait return ColorTrait( *args, **metadata ) Color = TraitFactory( Color ) def RGBColor ( *args, **metadata ): """ Returns a trait whose value must be a GUI toolkit-specific RGB-based color. Description ----------- For wxPython, the returned trait accepts any of the following values: * A tuple of the form (*r*, *g*, *b*), in which *r*, *g*, and *b* represent red, green, and blue values, respectively, and are floats in the range from 0.0 to 1.0 * An integer whose hexadecimal form is 0x*RRGGBB*, where *RR* is the red value, *GG* is the green value, and *BB* is the blue value Default Value ------------- For wxPython, (0.0, 0.0, 0.0) (that is, white) """ from traitsui.toolkit_traits import RGBColorTrait return RGBColorTrait( *args, **metadata ) RGBColor = TraitFactory( RGBColor ) def Font ( *args, **metadata ): """ Returns a trait whose value must be a GUI toolkit-specific font. Description ----------- For wxPython, the returned trait accepts any of the following: * a wx.Font instance * a wx.FontPtr instance * a string describing the font, including one or more of the font family, size, weight, style, and typeface name. Default Value ------------- For wxPython, 'Arial 10' """ from traitsui.toolkit_traits import FontTrait return FontTrait( *args, **metadata ) Font = TraitFactory( Font ) traits-4.6.0/traits/traits_listener.py000066400000000000000000001420521300633736300201320ustar00rootroot00000000000000#------------------------------------------------------------------------------- # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 03/05/2007 # #------------------------------------------------------------------------------- """ Defines classes used to implement and manage various trait listener patterns. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import import re import string import weakref from weakref import WeakKeyDictionary from string import whitespace from types import MethodType from .has_traits import HasPrivateTraits from .trait_base import Undefined, Uninitialized from .traits import Property from .trait_types import Str, Int, Bool, Instance, List, Enum, Any from .trait_errors import TraitError from .trait_notifiers import TraitChangeNotifyWrapper #--------------------------------------------------------------------------- # Constants: #--------------------------------------------------------------------------- # The name of the dictionary used to store active listeners TraitsListener = '__traits_listener__' # End of String marker EOS = '\0' # Types of traits that can be listened to ANYTRAIT_LISTENER = '_register_anytrait' SIMPLE_LISTENER = '_register_simple' LIST_LISTENER = '_register_list' DICT_LISTENER = '_register_dict' SET_LISTENER = '_register_set' # Mapping from trait default value types to listener types type_map = { 5: LIST_LISTENER, 6: DICT_LISTENER, 9: SET_LISTENER } # Listener types: ANY_LISTENER = 0 SRC_LISTENER = 1 DST_LISTENER = 2 ListenerType = { 0: ANY_LISTENER, 1: DST_LISTENER, 2: DST_LISTENER, 3: SRC_LISTENER, 4: SRC_LISTENER } # Invalid destination ( object, name ) reference marker (i.e. ambiguous): INVALID_DESTINATION = ( None, None ) # Regular expressions used by the parser: simple_pat = re.compile( r'^([a-zA-Z_]\w*)(\.|:)([a-zA-Z_]\w*)$' ) name_pat = re.compile( r'([a-zA-Z_]\w*)\s*(.*)' ) # Characters valid in a traits name: name_chars = string.ascii_letters + string.digits + '_' #------------------------------------------------------------------------------- # Utility functions: #------------------------------------------------------------------------------- def indent ( text, first_line = True, n = 1, width = 4 ): """ Indent lines of text. Parameters ---------- text : str The text to indent. first_line : bool, optional If False, then the first line will not be indented (default: True). n : int, optional The level of indentation (default: 1). width : int, optional The number of spaces in each level of indentation (default: 4). Returns ------- indented : str """ lines = text.split( '\n' ) if not first_line: first = lines[0] lines = lines[1:] spaces = ' ' * (width * n) lines2 = [ spaces + x for x in lines ] if not first_line: lines2.insert( 0, first ) indented = '\n'.join( lines2 ) return indented #------------------------------------------------------------------------------- # Metadata filters: #------------------------------------------------------------------------------- def is_not_none ( value ): return (value is not None) def is_none ( value ): return (value is None) def not_event ( value ): return (value != 'event') #------------------------------------------------------------------------------- # 'ListenerBase' class: #------------------------------------------------------------------------------- class ListenerBase ( HasPrivateTraits ): #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- # The handler to be called when any listened to trait is changed: #handler = Any # The dispatch mechanism to use when invoking the handler: #dispatch = Str # Does the handler go at the beginning (True) or end (False) of the # notification handlers list? #priority = Bool( False ) # The next level (if any) of ListenerBase object to be called when any of # our listened to traits is changed: #next = Instance( ListenerBase ) # The type of handler being used: #type = Enum( ANY_LISTENER, SRC_LISTENER, DST_LISTENER ) # Should changes to this item generate a notification to the handler? # notify = Bool # Should registering listeners for items reachable from this listener item # be deferred until the associated trait is first read or set? # deferred = Bool #--------------------------------------------------------------------------- # Registers new listeners: #--------------------------------------------------------------------------- def register ( self, new ): """ Registers new listeners. """ raise NotImplementedError #--------------------------------------------------------------------------- # Unregisters any existing listeners: #--------------------------------------------------------------------------- def unregister ( self, old ): """ Unregisters any existing listeners. """ raise NotImplementedError #--------------------------------------------------------------------------- # Handles a trait change for a simple trait: #--------------------------------------------------------------------------- def handle ( self, object, name, old, new ): """ Handles a trait change for a simple trait. """ raise NotImplementedError #--------------------------------------------------------------------------- # Handles a trait change for a list trait: #--------------------------------------------------------------------------- def handle_list ( self, object, name, old, new ): """ Handles a trait change for a list trait. """ raise NotImplementedError #--------------------------------------------------------------------------- # Handles a trait change for a list traits items: #--------------------------------------------------------------------------- def handle_list_items ( self, object, name, old, new ): """ Handles a trait change for a list traits items. """ raise NotImplementedError #--------------------------------------------------------------------------- # Handles a trait change for a dictionary trait: #--------------------------------------------------------------------------- def handle_dict ( self, object, name, old, new ): """ Handles a trait change for a dictionary trait. """ raise NotImplementedError #--------------------------------------------------------------------------- # Handles a trait change for a dictionary traits items: #--------------------------------------------------------------------------- def handle_dict_items ( self, object, name, old, new ): """ Handles a trait change for a dictionary traits items. """ raise NotImplementedError #------------------------------------------------------------------------------- # 'ListenerItem' class: #------------------------------------------------------------------------------- class ListenerItem ( ListenerBase ): #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- #: The name of the trait to listen to: name = Str #: The name of any metadata that must be present (or not present): metadata_name = Str #: Does the specified metadata need to be defined (True) or not defined #: (False)? metadata_defined = Bool( True ) #: The handler to be called when any listened-to trait is changed: handler = Any #: A weakref 'wrapped' version of 'handler': wrapped_handler_ref = Any #: The dispatch mechanism to use when invoking the handler: dispatch = Str #: Does the handler go at the beginning (True) or end (False) of the #: notification handlers list? priority = Bool( False ) #: The next level (if any) of ListenerBase object to be called when any of #: this object's listened-to traits is changed: next = Instance( ListenerBase ) #: The type of handler being used: type = Enum( ANY_LISTENER, SRC_LISTENER, DST_LISTENER ) #: Should changes to this item generate a notification to the handler? notify = Bool( True ) #: Should registering listeners for items reachable from this listener item #: be deferred until the associated trait is first read or set? deferred = Bool( False ) #: Is this an 'any_trait' change listener, or does it create explicit #: listeners for each individual trait? is_any_trait = Bool( False ) #: Is the associated handler a special list handler that handles both #: 'foo' and 'foo_items' events by receiving a list of 'deleted' and 'added' #: items as the 'old' and 'new' arguments? is_list_handler = Bool( False ) #: A dictionary mapping objects to a list of all current active #: (*name*, *type*) listener pairs, where *type* defines the type of #: listener, one of: (SIMPLE_LISTENER, LIST_LISTENER, DICT_LISTENER). active = Instance( WeakKeyDictionary, () ) #-- 'ListenerBase' Class Method Implementations ---------------------------- #--------------------------------------------------------------------------- # String representation: #--------------------------------------------------------------------------- def __repr__ ( self, seen = None ): """Returns a string representation of the object. Since the object graph may have cycles, we extend the basic __repr__ API to include a set of objects we've already seen while constructing a string representation. When this method tries to get the repr of a ListenerItem or ListenerGroup, we will use the extended API and build up the set of seen objects. The repr of a seen object will just be ''. """ if seen is None: seen = set() seen.add( self ) next_repr = 'None' next = self.next if next is not None: if next in seen: next_repr = '' else: next_repr = next.__repr__( seen ) return """%s( name = %r, metadata_name = %r, metadata_defined = %r, is_any_trait = %r, dispatch = %r, notify = %r, is_list_handler = %r, type = %r, next = %s, )""" % ( self.__class__.__name__, self.name, self.metadata_name, self.metadata_defined, self.is_any_trait, self.dispatch, self.notify, self.is_list_handler, self.type, indent( next_repr, False ) ) #--------------------------------------------------------------------------- # Registers new listeners: #--------------------------------------------------------------------------- def register ( self, new ): """ Registers new listeners. """ # Make sure we actually have an object to set listeners on and that it # has not already been registered (cycle breaking): if (new is None) or (new is Undefined) or (new in self.active): return INVALID_DESTINATION # Create a dictionary of {name: trait_values} that match the object's # definition for the 'new' object: name = self.name last = name[-1:] if last == '*': # Handle the special case of an 'anytrait' change listener: if self.is_any_trait: try: self.active[ new ] = [ ( '', ANYTRAIT_LISTENER ) ] return self._register_anytrait( new, '', False ) except TypeError: # This error can occur if 'new' is a list or other object # for which a weakref cannot be created as the dictionary # key for 'self.active': return INVALID_DESTINATION # Handle trait matching based on a common name prefix and/or # matching trait metadata: metadata = self._metadata if metadata is None: self._metadata = metadata = { 'type': not_event } if self.metadata_name != '': if self.metadata_defined: metadata[ self.metadata_name ] = is_not_none else: metadata[ self.metadata_name ] = is_none # Get all object traits with matching metadata: names = new.trait_names( **metadata ) # If a name prefix was specified, filter out only the names that # start with the specified prefix: name = name[:-1] if name != '': n = len( name ) names = [ aname for aname in names if name == aname[ : n ] ] # Create the dictionary of selected traits: bt = new.base_trait traits = dict( [ ( name, bt( name ) ) for name in names ] ) # Handle any new traits added dynamically to the object: new.on_trait_change( self._new_trait_added, 'trait_added' ) else: # Determine if the trait is optional or not: optional = (last == '?') if optional: name = name[:-1] # Else, no wildcard matching, just get the specified trait: trait = new.base_trait( name ) # Try to get the object trait: if trait is None: # Raise an error if trait is not defined and not optional: # fixme: Properties which are lists don't implement the # '..._items' sub-trait, which can cause a failure here when # used with an editor that sets up listeners on the items... if not optional: raise TraitError( "'%s' object has no '%s' trait" % ( new.__class__.__name__, name ) ) # Otherwise, just skip it: traits = {} else: # Create a result dictionary containing just the single trait: traits = { name: trait } # For each item, determine its type (simple, list, dict): self.active[ new ] = active = [] for name, trait in traits.items(): # Determine whether the trait type is simple, list, set or # dictionary: type = SIMPLE_LISTENER handler = trait.handler if handler is not None: type = type_map.get( handler.default_value_type, SIMPLE_LISTENER ) # Add the name and type to the list of traits being registered: active.append( ( name, type ) ) # Set up the appropriate trait listeners on the object for the # current trait: value = getattr( self, type )( new, name, False ) if len( traits ) == 1: return value return INVALID_DESTINATION #--------------------------------------------------------------------------- # Unregisters any existing listeners: #--------------------------------------------------------------------------- def unregister ( self, old ): """ Unregisters any existing listeners. """ if old is not None and old is not Uninitialized: try: active = self.active.pop( old, None ) if active is not None: for name, type in active: getattr( self, type )( old, name, True ) except TypeError: # An error can occur if 'old' is a list or other object for # which a weakref cannot be created and used an a key for # 'self.active': pass #--------------------------------------------------------------------------- # Handles a trait change for an intermediate link trait: #--------------------------------------------------------------------------- def handle_simple ( self, object, name, old, new ): """ Handles a trait change for an intermediate link trait. """ self.next.unregister( old ) self.next.register( new ) def handle_dst ( self, object, name, old, new ): """ Handles a trait change for an intermediate link trait when the notification is for the final destination trait. """ self.next.unregister( old ) object, name = self.next.register( new ) if old is not Uninitialized: if object is None: raise TraitError( "on_trait_change handler signature is " "incompatible with a change to an intermediate trait" ) wh = self.wrapped_handler_ref() if wh is not None: wh( object, name, old, getattr( object, name, Undefined ) ) #--------------------------------------------------------------------------- # Handles a trait change for a list (or set) trait: #--------------------------------------------------------------------------- def handle_list ( self, object, name, old, new ): """ Handles a trait change for a list (or set) trait. """ if old is not None and old is not Uninitialized: unregister = self.next.unregister for obj in old: unregister( obj ) register = self.next.register for obj in new: register( obj ) #--------------------------------------------------------------------------- # Handles a trait change for a list (or set) traits items: #--------------------------------------------------------------------------- def handle_list_items ( self, object, name, old, new ): """ Handles a trait change for items of a list (or set) trait. """ self.handle_list( object, name, new.removed, new.added ) def handle_list_items_special ( self, object, name, old, new ): """ Handles a trait change for items of a list (or set) trait with notification. """ wh = self.wrapped_handler_ref() if wh is not None: wh( object, name, new.removed, new.added ) #--------------------------------------------------------------------------- # Handles a trait change for a dictionary trait: #--------------------------------------------------------------------------- def handle_dict ( self, object, name, old, new ): """ Handles a trait change for a dictionary trait. """ if old is not Uninitialized: unregister = self.next.unregister for obj in old.values(): unregister( obj ) register = self.next.register for obj in new.values(): register( obj ) #--------------------------------------------------------------------------- # Handles a trait change for a dictionary traits items: #--------------------------------------------------------------------------- def handle_dict_items ( self, object, name, old, new ): """ Handles a trait change for items of a dictionary trait. """ self.handle_dict( object, name, new.removed, new.added ) if len( new.changed ) > 0: # If 'name' refers to the '_items' trait, then remove the '_items' # suffix to get the actual dictionary trait. # # fixme: Is there ever a case where 'name' *won't* refer to the # '_items' trait? if name.endswith('_items'): name = name[:-len('_items')] dict = getattr( object, name ) unregister = self.next.unregister register = self.next.register for key, obj in new.changed.items(): unregister( obj ) register( dict[ key ] ) #--------------------------------------------------------------------------- # Handles an invalid intermediate trait change to a handler that must be # applied to the final destination object.trait: #--------------------------------------------------------------------------- def handle_error ( self, obj, name, old, new ): """ Handles an invalid intermediate trait change to a handler that must be applied to the final destination object.trait. """ if old is not None and old is not Uninitialized: raise TraitError( "on_trait_change handler signature is " "incompatible with a change to an intermediate trait" ) #-- Event Handlers --------------------------------------------------------- #--------------------------------------------------------------------------- # Handles the 'handler' trait being changed: #--------------------------------------------------------------------------- def _handler_changed ( self, handler ): """ Handles the **handler** trait being changed. """ if self.next is not None: self.next.handler = handler #--------------------------------------------------------------------------- # Handles the 'wrapped_handler_ref' trait being changed: #--------------------------------------------------------------------------- def _wrapped_handler_ref_changed ( self, wrapped_handler_ref ): """ Handles the 'wrapped_handler_ref' trait being changed. """ if self.next is not None: self.next.wrapped_handler_ref = wrapped_handler_ref #--------------------------------------------------------------------------- # Handles the 'dispatch' trait being changed: #--------------------------------------------------------------------------- def _dispatch_changed ( self, dispatch ): """ Handles the **dispatch** trait being changed. """ if self.next is not None: self.next.dispatch = dispatch #--------------------------------------------------------------------------- # Handles the 'priority' trait being changed: #--------------------------------------------------------------------------- def _priority_changed ( self, priority ): """ Handles the **priority** trait being changed. """ if self.next is not None: self.next.priority = priority #-- Private Methods -------------------------------------------------------- #--------------------------------------------------------------------------- # Registers any 'anytrait' listener: #--------------------------------------------------------------------------- def _register_anytrait ( self, object, name, remove ): """ Registers any 'anytrait' listener. """ handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) return ( object, name ) #--------------------------------------------------------------------------- # Registers a handler for a simple trait: #--------------------------------------------------------------------------- def _register_simple ( self, object, name, remove ): """ Registers a handler for a simple trait. """ next = self.next if next is None: handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) return ( object, name ) tl_handler = self.handle_simple if self.notify: if self.type == DST_LISTENER: if self.dispatch != 'same': raise TraitError( "Trait notification dispatch type '%s' " "is not compatible with handler signature and " "extended trait name notification style" % self.dispatch ) tl_handler = self.handle_dst else: handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) object._on_trait_change( tl_handler, name, remove=remove, dispatch='extended', priority=self.priority, target=self._get_target(), ) if remove: return next.unregister( getattr( object, name ) ) if not self.deferred or name in object.__dict__: # Sometimes, the trait may already be assigned. This can happen when # there are chains of dynamic initializers and 'delegate' # notifications. If 'trait_a' and 'trait_b' have dynamic # initializers and 'trait_a's initializer creates 'trait_b', *and* # we have a DelegatesTo trait that delegates to 'trait_a', then the # listener that implements the delegate will create 'trait_a' and # thus 'trait_b'. If we are creating an extended trait change # listener on 'trait_b.something', and the 'trait_a' delegate # listeners just happen to get hooked up before this one, then # 'trait_b' will have been initialized already, and the registration # that we are deferring will never happen. return next.register( getattr( object, name ) ) return ( object, name ) #--------------------------------------------------------------------------- # Registers a handler for a list trait: #--------------------------------------------------------------------------- def _register_list ( self, object, name, remove ): """ Registers a handler for a list trait. """ next = self.next if next is None: handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) if self.is_list_handler: object._on_trait_change( self.handle_list_items_special, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) elif self.type == ANY_LISTENER: object._on_trait_change( handler, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) return ( object, name ) tl_handler = self.handle_list tl_handler_items = self.handle_list_items if self.notify: if self.type == DST_LISTENER: tl_handler = tl_handler_items = self.handle_error else: handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) if self.is_list_handler: object._on_trait_change( self.handle_list_items_special, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) elif self.type == ANY_LISTENER: object._on_trait_change( handler, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) object._on_trait_change( tl_handler, name, remove=remove, dispatch='extended', priority=self.priority, target=self._get_target(), ) object._on_trait_change( tl_handler_items, name + '_items', remove=remove, dispatch='extended', priority=self.priority, target=self._get_target(), ) if remove: handler = next.unregister elif self.deferred: return INVALID_DESTINATION else: handler = next.register for obj in getattr( object, name ): handler( obj ) return INVALID_DESTINATION # Handle 'sets' the same as 'lists': # Note: Currently the behavior of sets is almost identical to that of lists, # so we are able to share the same code for both. This includes some 'duck # typing' that occurs with the TraitListEvent and TraitSetEvent, that define # 'removed' and 'added' attributes that behave similarly enough (from the # point of view of this module) that they can be treated as equivalent. If # the behavior of sets ever diverges from that of lists, then this code may # need to be changed. _register_set = _register_list #--------------------------------------------------------------------------- # Registers a handler for a dictionary trait: #--------------------------------------------------------------------------- def _register_dict ( self, object, name, remove ): """ Registers a handler for a dictionary trait. """ next = self.next if next is None: handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) if self.type == ANY_LISTENER: object._on_trait_change( handler, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) return ( object, name ) tl_handler = self.handle_dict tl_handler_items = self.handle_dict_items if self.notify: if self.type == DST_LISTENER: tl_handler = tl_handler_items = self.handle_error else: handler = self.handler() if handler is not Undefined: object._on_trait_change( handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) if self.type == ANY_LISTENER: object._on_trait_change( handler, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) object._on_trait_change( tl_handler, name, remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) object._on_trait_change( tl_handler_items, name + '_items', remove=remove, dispatch=self.dispatch, priority=self.priority, target=self._get_target(), ) if remove: handler = next.unregister elif self.deferred: return INVALID_DESTINATION else: handler = next.register for obj in getattr( object, name ).values(): handler( obj ) return INVALID_DESTINATION #--------------------------------------------------------------------------- # Handles new traits being added to an object being monitored: #--------------------------------------------------------------------------- def _new_trait_added ( self, object, name, new_trait ): """ Handles new traits being added to an object being monitored. """ # Set if the new trait matches our prefix and metadata: if new_trait.startswith( self.name[:-1] ): trait = object.base_trait( new_trait ) for meta_name, meta_eval in self._metadata.items(): if not meta_eval( getattr( trait, meta_name ) ): return # Determine whether the trait type is simple, list, set or # dictionary: type = SIMPLE_LISTENER handler = trait.handler if handler is not None: type = type_map.get( handler.default_value_, SIMPLE_LISTENER ) # Add the name and type to the list of traits being registered: self.active[ object ].append( ( new_trait, type ) ) # Set up the appropriate trait listeners on the object for the # new trait: getattr( self, type )( object, new_trait, False ) def _get_target(self): """ Get the target object from the ListenerNotifyWrapper. """ target = None lnw = self.wrapped_handler_ref() if lnw is not None: target_ref = getattr(lnw, 'object', None) if target_ref is not None: target = target_ref() return target #------------------------------------------------------------------------------- # 'ListenerGroup' class: #------------------------------------------------------------------------------- def _set_value ( self, name, value ): for item in self.items: setattr( item, name, value ) def _get_value ( self, name ): # Use the attribute on the first item. If there are no items, return None. if self.items: return getattr( self.items[0], name ) else: return None ListProperty = Property( fget = _get_value, fset = _set_value ) class ListenerGroup ( ListenerBase ): #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- #: The handler to be called when any listened-to trait is changed handler = Property #: A weakref 'wrapped' version of 'handler': wrapped_handler_ref = Property #: The dispatch mechanism to use when invoking the handler: dispatch = Property #: Does the handler go at the beginning (True) or end (False) of the #: notification handlers list? priority = ListProperty #: The next level (if any) of ListenerBase object to be called when any of #: this object's listened-to traits is changed next = ListProperty #: The type of handler being used: type = ListProperty #: Should changes to this item generate a notification to the handler? notify = ListProperty #: Should registering listeners for items reachable from this listener item #: be deferred until the associated trait is first read or set? deferred = ListProperty # The list of ListenerBase objects in the group items = List( ListenerBase ) #-- Property Implementations ----------------------------------------------- def _set_handler ( self, handler ): if self._handler is None: self._handler = handler for item in self.items: item.handler = handler def _set_wrapped_handler_ref ( self, wrapped_handler_ref ): if self._wrapped_handler_ref is None: self._wrapped_handler_ref = wrapped_handler_ref for item in self.items: item.wrapped_handler_ref = wrapped_handler_ref def _set_dispatch ( self, dispatch ): if self._dispatch is None: self._dispatch = dispatch for item in self.items: item.dispatch = dispatch #-- 'ListenerBase' Class Method Implementations ---------------------------- #--------------------------------------------------------------------------- # String representation: #--------------------------------------------------------------------------- def __repr__ ( self, seen = None ): """Returns a string representation of the object. Since the object graph may have cycles, we extend the basic __repr__ API to include a set of objects we've already seen while constructing a string representation. When this method tries to get the repr of a ListenerItem or ListenerGroup, we will use the extended API and build up the set of seen objects. The repr of a seen object will just be ''. """ if seen is None: seen = set() seen.add( self ) lines = [ '%s(items = [' % self.__class__.__name__ ] for item in self.items: lines.extend( indent( item.__repr__( seen ), True ).split( '\n' ) ) lines[-1] += ',' lines.append( '])' ) return '\n'.join( lines ) #--------------------------------------------------------------------------- # Registers new listeners: #--------------------------------------------------------------------------- def register ( self, new ): """ Registers new listeners. """ for item in self.items: item.register( new ) return INVALID_DESTINATION #--------------------------------------------------------------------------- # Unregisters any existing listeners: #--------------------------------------------------------------------------- def unregister ( self, old ): """ Unregisters any existing listeners. """ for item in self.items: item.unregister( old ) #------------------------------------------------------------------------------- # 'ListenerParser' class: #------------------------------------------------------------------------------- class ListenerParser ( HasPrivateTraits ): #------------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------------- #: The string being parsed text = Str #: The length of the string being parsed. len_text = Int #: The current parse index within the string index = Int #: The next character from the string being parsed next = Property #: The next Python attribute name within the string: name = Property #: The next non-whitespace character skip_ws = Property #: Backspaces to the last character processed backspace = Property #: The ListenerBase object resulting from parsing **text** listener = Instance( ListenerBase ) #-- Property Implementations ----------------------------------------------- def _get_next ( self ): index = self.index self.index += 1 if index >= self.len_text: return EOS return self.text[ index ] def _get_backspace ( self ): self.index = max( 0, self.index - 1 ) def _get_skip_ws ( self ): while True: c = self.next if c not in whitespace: return c def _get_name ( self ): match = name_pat.match( self.text, self.index - 1 ) if match is None: return '' self.index = match.start( 2 ) return match.group( 1 ) #-- object Method Overrides ------------------------------------------------ def __init__ ( self, text = '', **traits ): self.text = text super( ListenerParser, self ).__init__( **traits ) #-- Private Methods -------------------------------------------------------- #--------------------------------------------------------------------------- # Parses the text and returns the appropriate collection of ListenerBase # objects described by the text: #--------------------------------------------------------------------------- def parse ( self ): """ Parses the text and returns the appropriate collection of ListenerBase objects described by the text. """ # Try a simple case of 'name1.name2'. The simplest case of a single # Python name never triggers this parser, so we don't try to make that # a shortcut too. Whitespace should already have been stripped from the # start and end. # TODO: The use of regexes should be used throughout all of the parsing # functions to speed up all aspects of parsing. match = simple_pat.match( self.text ) if match is not None: return ListenerItem( name = match.group( 1 ), notify = match.group(2) == '.', next = ListenerItem( name = match.group( 3 ) ) ) return self.parse_group( EOS ) #--------------------------------------------------------------------------- # Parses the contents of a group: #--------------------------------------------------------------------------- def parse_group ( self, terminator = ']' ): """ Parses the contents of a group. """ items = [] while True: items.append( self.parse_item( terminator ) ) c = self.skip_ws if c == terminator: break if c != ',': if terminator == EOS: self.error( "Expected ',' or end of string" ) else: self.error( "Expected ',' or '%s'" % terminator ) if len( items ) == 1: return items[0] return ListenerGroup( items = items ) #--------------------------------------------------------------------------- # Parses a single, complete listener item/group string: #--------------------------------------------------------------------------- def parse_item ( self, terminator ): """ Parses a single, complete listener item or group string. """ c = self.skip_ws if c == '[': result = self.parse_group() c = self.skip_ws else: name = self.name if name != '': c = self.next result = ListenerItem( name = name ) if c in '+-': result.name += '*' result.metadata_defined = (c == '+') cn = self.skip_ws result.metadata_name = metadata = self.name if metadata != '': cn = self.skip_ws result.is_any_trait = ((c == '-') and (name == '') and (metadata == '')) c = cn if result.is_any_trait and (not ((c == terminator) or ((c == ',') and (terminator == ']')))): self.error( "Expected end of name" ) elif c == '?': if len( name ) == 0: self.error( "Expected non-empty name preceding '?'" ) result.name += '?' c = self.skip_ws cycle = (c == '*') if cycle: c = self.skip_ws if c in '.:': result.notify = (c == '.') next = self.parse_item( terminator ) if cycle: last = result while last.next is not None: last = last.next last.next = lg = ListenerGroup( items = [ next, result ] ) result = lg else: result.next = next return result if c == '[': is_closing_bracket = (self.skip_ws == ']') next_char = self.skip_ws item_complete = (next_char == terminator or next_char == ',') if is_closing_bracket and item_complete: self.backspace result.is_list_handler = True else: self.error( "Expected '[]' at the end of an item" ) else: self.backspace if cycle: result.next = result return result #--------------------------------------------------------------------------- # Parses the metadata portion of a listener item: #--------------------------------------------------------------------------- def parse_metadata ( self, item ): """ Parses the metadata portion of a listener item. """ self.skip_ws item.metadata_name = name = self.name if name == '': self.backspace #--------------------------------------------------------------------------- # Raises a syntax error: #--------------------------------------------------------------------------- def error ( self, msg ): """ Raises a syntax error. """ raise TraitError( "%s at column %d of '%s'" % ( msg, self.index, self.text ) ) #-- Event Handlers --------------------------------------------------------- #--------------------------------------------------------------------------- # Handles the 'text' trait being changed: #--------------------------------------------------------------------------- def _text_changed ( self ): self.index = 0 self.len_text = len( self.text ) self.listener = self.parse() #------------------------------------------------------------------------------- # 'ListenerNotifyWrapper' class: #------------------------------------------------------------------------------- class ListenerNotifyWrapper ( TraitChangeNotifyWrapper ): #-- TraitChangeNotifyWrapper Method Overrides ------------------------------ def __init__ ( self, handler, owner, id, listener, target=None): self.type = ListenerType.get( self.init( handler, weakref.ref( owner, self.owner_deleted ), target ) ) self.id = id self.listener = listener def listener_deleted ( self, ref ): owner = self.owner() if owner is not None: dict = owner.__dict__.get( TraitsListener ) listeners = dict.get( self.id ) listeners.remove( self ) if len( listeners ) == 0: del dict[ self.id ] if len( dict ) == 0: del owner.__dict__[ TraitsListener ] # fixme: Is the following line necessary, since all registered # notifiers should be getting the same 'listener_deleted' call: self.listener.unregister( owner ) self.object = self.owner = self.listener = None def owner_deleted ( self, ref ): self.object = self.owner = None #------------------------------------------------------------------------------- # 'ListenerHandler' class: #------------------------------------------------------------------------------- class ListenerHandler ( object ): def __init__ ( self, handler ): if type( handler ) is MethodType: object = handler.im_self if object is not None: self.object = weakref.ref( object, self.listener_deleted ) self.name = handler.__name__ return self.handler = handler def __call__ ( self ): result = getattr( self, 'handler', None ) if result is not None: return result return getattr( self.object(), self.name ) def listener_deleted ( self, ref ): self.handler = Undefined traits-4.6.0/traits/ustr_trait.py000066400000000000000000000144241300633736300171200ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2008, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: David C. Morrill # Date: 08/21/2008 # #------------------------------------------------------------------------------ """ Defines the UStr type and HasUniqueStrings mixin class for efficiently creating lists of objects containing traits whose string values must be unique within the list. """ #------------------------------------------------------------------------------- # Imports: #------------------------------------------------------------------------------- from __future__ import absolute_import from .trait_base import is_str from .has_traits import HasTraits from .trait_value import TraitValue, TypeValue from .trait_types import List from .trait_handlers import TraitType, NoDefaultSpecified #------------------------------------------------------------------------------- # 'UStr' class: #------------------------------------------------------------------------------- class UStr ( TraitType ): """ Trait type that ensures that a value assigned to a trait is unique within the list it belongs to. """ #: The type value to assign to restore the original list item type when a #: list item is removed from the monitored list: str_type = TraitValue() #: The informational text describing the trait: info_text = 'a unique string' def __init__ ( self, owner, list_name, str_name, default_value = NoDefaultSpecified, **metadata ): """ Initializes the type. """ super( UStr, self ).__init__( default_value, **metadata ) self.owner = owner self.list_name = list_name self.str_name = str_name self.ustr_type = TypeValue( self ) self.names = dict( [ ( getattr( item, str_name ), item ) for item in getattr( owner, list_name ) ] ) self.roots = {} self.available = {} owner.on_trait_change( self._items_modified, list_name + '[]' ) def validate ( self, object, name, value ): """ Ensures that a value being assigned to a trait is a unique string. """ if isinstance( value, basestring ): names = self.names old_name = getattr( object, name ) if names.get( old_name ) is object: self._remove( old_name ) if value not in names: names[ value ] = object return value available = self.available.get( value ) while True: if available is None: new_value = None break index = available.pop() if len( available ) == 0: del self.available[ value ] available = None new_value = '%s_%d' % ( value, index ) if new_value not in names: break if new_value is None: self.roots[ value ] = index = \ self.roots.setdefault( value, 1 ) + 1 new_value = '%s_%d' % ( value, index ) names[ new_value ] = object return new_value self.error( object, name, value ) def _remove ( self, name ): """ Removes a specified name. """ self.names.pop( name, None ) col = name.rfind( '_' ) if col >= 0: try: index = int( name[ col + 1: ] ) prefix = name[ : col ] if prefix in self.roots: if prefix not in self.available: self.available[ prefix ] = set() self.available[ prefix ].add( index ) except: pass def _items_modified ( self, object, name, removed, added ): """ Handles items being added to or removed from the monitored list. """ str_name = self.str_name str_type = self.str_type ustr_type = self.ustr_type for item in removed: setattr( item, str_name, str_type ) self._remove( getattr( item, str_name ) ) for item in added: setattr( item, str_name, ustr_type ) setattr( item, str_name, getattr( item, str_name ) ) #------------------------------------------------------------------------------- # 'HasUniqueStrings' class: #------------------------------------------------------------------------------- class HasUniqueStrings ( HasTraits ): """ Mixin or base class for objects containing lists with items containing string valued traits that must be unique. List traits within the class that contain items which have string traits which must be unique should indicate this by attaching metadata of the form:: unique_string = 'trait1, trait2, ..., traitn' where each 'traiti' value is the name of a trait within each list item that must contain unique string data. For example:: usa = List( State, unique_string = 'name, abbreviation' ) """ #-- Private Traits --------------------------------------------------------- # List of UStr traits that have been attached to object list traits: _ustr_traits = List #-- HasTraits Object Initializer ------------------------------------------- def traits_init ( self ): """ Adds any UStrMonitor objects to list traits with 'unique_string' metadata. """ super( HasUniqueStrings, self ).traits_init() for name, trait in self.traits( unique_string = is_str ).items(): for str_name in trait.unique_string.split( ',' ): self._ustr_traits.append( UStr( self, name, str_name.strip() ) ) items = getattr( self, name ) if len( items ) > 0: setattr( self, name, [] ) setattr( self, name, items ) traits-4.6.0/traits/util/000077500000000000000000000000001300633736300153165ustar00rootroot00000000000000traits-4.6.0/traits/util/__init__.py000066400000000000000000000013441300633736300174310ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2003-2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """ Utility functions, part of the Traits project. :Copyright: 2003-2013 Enthought, Inc. """ traits-4.6.0/traits/util/api.py000066400000000000000000000002771300633736300164470ustar00rootroot00000000000000__all__ = [ 'deprecated', 'import_symbol', 'record_events', ] from .deprecated import deprecated from .event_tracer import record_events from .import_symbol import import_symbol traits-4.6.0/traits/util/async_trait_wait.py000066400000000000000000000037721300633736300212450ustar00rootroot00000000000000import threading def wait_for_condition(condition, obj, trait, timeout=None): """ Wait until the given condition is true, re-evaluating on trait change. This is intended for use in multithreading situations where traits can be modified from a different thread than the calling thread. Wait until `condition` is satisfied. Raise a RuntimeError if `condition` is not satisfied within the given timeout. `condition` is a callback function that will be called with `obj` as its single argument. It should return a boolean indicating whether the condition is satisfied or not. `timeout` gives the maximum time in seconds to wait for the condition to become true. The default value of `None` indicates no timeout. (obj, trait) give an object and trait to listen to for indication of a possible change: whenever the trait changes, the condition is re-evaluated. The condition will also be evaluated on entering this function. Note that in cases of unusual timing it's possible for the condition to be evaluated one more time *after* the ``wait_for_condition`` call has returned. """ condition_satisfied = threading.Event() def handler(): if condition(obj): condition_satisfied.set() obj.on_trait_change(handler, trait) try: if condition(obj): # Catch case where the condition was satisfied before # the on_trait_change handler was active. pass elif timeout is None: # Allow a Ctrl-C to interrupt. The 0.05 value matches # what's used by the standard library's Condition.wait. while not condition_satisfied.is_set(): condition_satisfied.wait(timeout=0.05) else: condition_satisfied.wait(timeout=timeout) if not condition_satisfied.is_set(): raise RuntimeError("Timed out waiting for condition.") finally: obj.on_trait_change(handler, trait, remove=True) traits-4.6.0/traits/util/camel_case.py000066400000000000000000000036201300633736300177450ustar00rootroot00000000000000""" Defines utility functions for operating on camel case names. """ # Standard library imports. import re ############################################################################### # Classes ############################################################################### class CamelCaseToPython: """ Simple functor class to convert names from camel case to idiomatic Python variable names. For example:: >>> camel2python = CamelCaseToPython >>> camel2python('XMLActor2DToSGML') 'xml_actor2d_to_sgml' """ def __init__(self): self.patn = re.compile(r'([A-Z0-9]+)([a-z0-9]*)') self.nd_patn = re.compile(r'(\D[123])_D') def __call__(self, name): ret = self.patn.sub(self._repl, name) ret = self.nd_patn.sub(r'\1d', ret) if ret[0] == '_': ret = ret[1:] return ret.lower() def _repl(self, m): g1 = m.group(1) g2 = m.group(2) if len(g1) > 1: if g2: return '_' + g1[:-1] + '_' + g1[-1] + g2 else: return '_' + g1 else: return '_' + g1 + g2 ############################################################################### # Functions ############################################################################### # Instantiate a converter. camel_case_to_python = CamelCaseToPython() def camel_case_to_words(s): """ Convert a camel case string into words separated by spaces. For example:: >>> camel_case_to_words('CamelCase') 'Camel Case' """ def add_space_between_words(s, c): # We detect a word boundary if the character we are looking at is # upper case, but the character preceding it is lower case. if len(s) > 0 and s[-1].islower() and c.isupper(): return s + ' ' + c return s + c return reduce(add_space_between_words, s, '') traits-4.6.0/traits/util/clean_strings.py000066400000000000000000000052071300633736300205270ustar00rootroot00000000000000#----------------------------------------------------------------------------- # # Copyright (c) 2006 by Enthought, Inc. # All rights reserved. # #----------------------------------------------------------------------------- """ Provides functions that mange strings to avoid characters that would be problematic in certain situations. """ # Standard library imports. import copy import datetime import keyword import re def clean_filename(name): """ Munge a string to avoid characters that might be problematic as a filename in some filesystems. """ # The only acceptable characters are alphanumeric (in the current locale) # plus a period and dash. wordparts = re.split('[^\w\.\-]+', name) # Filter out empty strings at the beginning or end of the list. wordparts = filter(None, wordparts) # Make sure this is an ASCII-encoded string, not a Unicode string. filename = '_'.join(wordparts).encode('ascii') return filename def clean_timestamp(dt=None, microseconds=False): """ Return a timestamp that has been cleansed of characters that might cause problems in filenames, namely colons. If no datetime object is provided, then uses the current time. Description ----------- The timestamp is in ISO-8601 format with the following exceptions: * Colons ':' are replaced by underscores '_'. * Microseconds are not displayed if the 'microseconds' parameter is False. Parameters ---------- dt : None or datetime.datetime If None, then the current time is used. microseconds : bool Display microseconds or not. Returns ------- A string timestamp. """ if dt is None: dt = datetime.datetime.now() else: # Operate on a copy. dt = copy.copy(dt) if not microseconds: # The microseconds are largely uninformative but annoying. dt = dt.replace(microsecond=0) stamp = dt.isoformat().replace(':', '_') return stamp def python_name(name): """ Attempt to make a valid Python identifier out of a name. """ if len(name) > 0: # Replace spaces with underscores. name = name.replace(' ', '_').lower() # If the name is a Python keyword then prefix it with an # underscore. if keyword.iskeyword(name): name = '_' + name # If the name starts with a digit then prefix it with an # underscore. if name[0].isdigit(): name = '_' + name return name ### EOF ###################################################################### traits-4.6.0/traits/util/deprecated.py000066400000000000000000000023301300633736300177660ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005-2014, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in /LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! #------------------------------------------------------------------------------ """ A decorator for marking methods/functions as deprecated. """ # Standard library imports. import functools import warnings def deprecated(message): """ A factory for decorators for marking methods/functions as deprecated. """ def decorator(fn): """ A decorator for marking methods/functions as deprecated. """ @functools.wraps(fn) def wrapper(*args, **kw): """ The method/function wrapper. """ warnings.warn(message, DeprecationWarning, stacklevel=2) return fn(*args, **kw) return wrapper return decorator #### EOF ###################################################################### traits-4.6.0/traits/util/event_tracer.py000066400000000000000000000246621300633736300203630ustar00rootroot00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2013, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # #------------------------------------------------------------------------------ """ Record trait change events in single and multi-threaded environments. """ import inspect import os import threading from contextlib import contextmanager from datetime import datetime from traits import trait_notifiers CHANGEMSG = ( u"{time} {direction:-{direction}{length}} {name!r} changed from " u"{old!r} to {new!r} in {class_name!r}\n") CALLINGMSG = u"{time} {action:>{gap}}: {handler!r} in {source}\n" EXITMSG = ( u"{time} {direction:-{direction}{length}} " u"EXIT: {handler!r}{exception}\n") SPACES_TO_ALIGN_WITH_CHANGE_MESSAGE = 9 class SentinelRecord(object): """ Sentinel record to separate groups of chained change event dispatches. """ __slots__ = () def __unicode__(self): return u'\n' class ChangeMessageRecord(object): """ Message record for a change event dispatch. """ __slots__ = ('time', 'indent', 'name', 'old', 'new', 'class_name') def __init__(self, time, indent, name, old, new, class_name): #: Time stamp in UTC. self.time = time #: Depth level in a chain of trait change dispatches. self.indent = indent #: The name of the trait that changed self.name = name #: The old value. self.old = old #: The new value. self.new = new #: The name of the class that the trait change took place. self.class_name = class_name def __unicode__(self): length = self.indent * 2 return CHANGEMSG.format( time=self.time, direction='>', name=self.name, old=self.old, new=self.new, class_name=self.class_name, length=length, ) class CallingMessageRecord(object): """ Message record for a change handler call. """ __slots__ = ('time', 'indent', 'handler', 'source') def __init__(self, time, indent, handler, source): #: Time stamp in UTC. self.time = time #: Depth level of the call in a chain of trait change dispatches. self.indent = indent #: The traits change handler that is called. self.handler = handler #: The source file where the handler was defined. self.source = source def __unicode__(self): gap = self.indent * 2 + SPACES_TO_ALIGN_WITH_CHANGE_MESSAGE return CALLINGMSG.format( time=self.time, action='CALLING', handler=self.handler, source=self.source, gap=gap) class ExitMessageRecord(object): """ Message record for returning from a change event dispatch. """ __slots__ = ('time', 'indent', 'handler', 'exception') def __init__(self, time, indent, handler, exception): #: Time stamp in UTC. self.time = time #: Depth level of the exit in a chain of trait change dispatch. self.indent = indent #: The traits change handler that is called. self.handler = handler #: The exception type (if one took place) self.exception = exception def __unicode__(self): length = self.indent * 2 return EXITMSG.format( time=self.time, direction='<', handler=self.handler, exception=self.exception, length=length, ) class RecordContainer(object): """ A simple record container. This class is commonly used to hold records from a single thread. """ def __init__(self): self._records = [] def record(self, record): """ Add the record into the container. """ self._records.append(record) def save_to_file(self, filename): """ Save the records into a file. """ with open(filename, 'w') as fh: for record in self._records: fh.write(unicode(record)) class MultiThreadRecordContainer(object): """ A container of record containers that are used by separate threads. Each record container is mapped to a thread name id. When a RecordContainer does not exist for a specific thread a new empty RecordContainer will be created on request. """ def __init__(self): self._creation_lock = threading.Lock() self._record_containers = {} def get_change_event_collector(self, thread_name): """ Return the dedicated RecordContainer for the thread. If no RecordContainer is found for `thread_name` then a new RecordContainer is created. """ with self._creation_lock: container = self._record_containers.get(thread_name) if container is None: container = RecordContainer() self._record_containers[thread_name] = container return container def save_to_directory(self, directory_name): """ Save records files into the directory. Each RecordContainer will dump its records on a separate file named .trace. """ with self._creation_lock: containers = self._record_containers for thread_name, container in containers.iteritems(): filename = os.path.join( directory_name, '{0}.trace'.format(thread_name)) container.save_to_file(filename) class ChangeEventRecorder(object): """ A single thread trait change event recorder. """ def __init__(self, container): """ Class constructor Parameters ---------- container : MultiThreadRecordContainer An container to store the records for each trait change. """ self.indent = 1 self.container = container def pre_tracer(self, obj, name, old, new, handler): """ Record a string representation of the trait change dispatch """ indent = self.indent time = datetime.utcnow().isoformat(' ') container = self.container container.record( ChangeMessageRecord( time=time, indent=indent, name=name, old=old, new=new, class_name=obj.__class__.__name__, ), ) container.record( CallingMessageRecord( time=time, indent=indent, handler=handler.__name__, source=inspect.getsourcefile(handler), ), ) self.indent += 1 def post_tracer(self, obj, name, old, new, handler, exception=None): """ Record a string representation of the trait change return """ time = datetime.utcnow().isoformat(' ') self.indent -= 1 indent = self.indent if exception: exception_msg = ' [EXCEPTION: {}]'.format(exception) else: exception_msg = '' container = self.container container.record( ExitMessageRecord( time=time, indent=indent, handler=handler.__name__, exception=exception_msg, ), ) if indent == 1: container.record(SentinelRecord()) class MultiThreadChangeEventRecorder(object): """ A thread aware trait change recorder. The class manages multiple ChangeEventRecorders which record trait change events for each thread in a separate file. """ def __init__(self, container): """ Object constructor Parameters ---------- container : MultiThreadChangeEventRecorder The container of RecordContainers to keep the trait change records for each thread. """ self.tracers = {} self._tracer_lock = threading.Lock() self.container = container def close(self): """ Close and stop all logging. """ with self._tracer_lock: self.tracers = {} def pre_tracer(self, obj, name, old, new, handler): """ The traits pre event tracer. This method should be set as the global pre event tracer for traits. """ tracer = self._get_tracer() tracer.pre_tracer(obj, name, old, new, handler) def post_tracer(self, obj, name, old, new, handler, exception=None): """ The traits post event tracer. This method should be set as the global post event tracer for traits. """ tracer = self._get_tracer() tracer.post_tracer(obj, name, old, new, handler, exception=exception) def _get_tracer(self): with self._tracer_lock: thread = threading.current_thread().name if thread not in self.tracers: container = self.container thread_container = container.get_change_event_collector( thread) tracer = ChangeEventRecorder(thread_container) self.tracers[thread] = tracer return tracer else: return self.tracers[thread] @contextmanager def record_events(): """ Multi-threaded trait change event tracer. Usage ----- :: >>> from trace_recorder import record_events >>> with record_events() as change_event_container: ... my_model.some_trait = True >>> change_event_container.save_to_directory('C:\\dev\\trace') This will install a tracer that will record all events that occur from setting of some_trait on the my_model instance. The results will be stored in one file per running thread in the directory 'C:\\dev\\trace'. The files are named after the thread being traced. """ container = MultiThreadRecordContainer() recorder = MultiThreadChangeEventRecorder(container=container) trait_notifiers.set_change_event_tracers( pre_tracer=recorder.pre_tracer, post_tracer=recorder.post_tracer) try: yield container finally: trait_notifiers.clear_change_event_tracers() recorder.close() traits-4.6.0/traits/util/home_directory.py000066400000000000000000000026251300633736300207110ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, 2006 by Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ import os def get_home_directory(): """ Determine the user's home directory.""" # 'HOME' should work on most Unixes, and 'USERPROFILE' works on at # least Windows XP ;^) # # FIXME: Is this really better than the following?? # path = os.path.expanduser('~') # The above seems to work on both Windows and Unixes though the docs # indicate it might not work as well on Macs. for name in ['HOME', 'USERPROFILE']: if name in os.environ: # Make sure that the path ends with a path separator. path = os.environ[name] if path[-1] != os.path.sep: path += os.path.sep break # If all else fails, the current directory will do. else: path = '' return path traits-4.6.0/traits/util/import_symbol.py000066400000000000000000000020471300633736300205720ustar00rootroot00000000000000""" A function to import symbols. """ def import_symbol(symbol_path): """ Import the symbol defined by the specified symbol path. Examples -------- import_symbol('tarfile:TarFile') -> TarFile import_symbol('tarfile:TarFile.open') -> TarFile.open To allow compatibility with old-school traits symbol names we also allow all-dotted paths, but in this case you can only import top-level names from the module. import_symbol('tarfile.TarFile') -> TarFile """ if ':' in symbol_path: module_name, symbol_name = symbol_path.split(':') module = __import__(module_name, {}, {}, [symbol_name], 0) symbol = eval(symbol_name, module.__dict__) else: components = symbol_path.split('.') module_name = '.'.join(components[:-1]) symbol_name = components[-1] module = __import__(module_name, {}, {}, [symbol_name], 0) symbol = getattr(module, symbol_name) return symbol #### EOF ###################################################################### traits-4.6.0/traits/util/resource.py000066400000000000000000000163401300633736300175230ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """ Utility functions for managing and finding resources (ie. images/files etc). get_path : Returns the absolute path of a class or instance create_unique_name : Creates a name with a given prefix that is not in a given list of existing names. The separator between the prefix and the rest of the name can also be specified (default is a '_') find_resource: Given a setuptools project specification string ('MyProject>=2.1') and a partial path leading from the projects base directory to the desired resource, will return either an opened file object or, if specified, a full path to the resource. """ # Standard library imports. import inspect, os, sys from distutils.sysconfig import get_python_lib def get_path(path): """ Returns an absolute path for the specified path. 'path' can be a string, class or instance. """ if type(path) is not str: # Is this a class or an instance? if inspect.isclass(path): klass = path else: klass = path.__class__ # Get the name of the module that the class was loaded from. module_name = klass.__module__ # Look the module up. module = sys.modules[module_name] if module_name == '__main__': dirs = [os.path.dirname(sys.argv[0]), os.getcwd()] for d in dirs: if os.path.exists(d): path = d break else: # Get the path to the module. path = os.path.dirname(module.__file__) return path def create_unique_name(prefix, names, separator='_'): """ Creates a name starting with 'prefix' that is not in 'names'. """ i = 1 name = prefix while name in names: name = prefix + separator + str(i) i += 1 return name def find_resource(project, resource_path, alt_path=None, return_path=False): """ Returns a file object or file path pointing to the desired resource. Parameters ---------- project : str The name of the project to look for the resource in. Can be the name or a requirement string. Ex: 'MyProject', 'MyProject>1.0', 'MyProject==1.1' resource_path : str The path to the file from inside the package. If the file desired is MyProject/data/image.jpg, resource_path would be 'data/image.jpg'. alt_path : str The path to the resource relative to the location of the application's top-level script (the one with __main__). If this function is called in code/scripts/myscript.py and the resource is code/data/image.jpg, the alt_path would be '../data/image.jpg'. This path is only used if the resource cannot be found using setuptools. return_path : bool Determines whether the function should return a file object or a full path to the resource. Returns ------- file : file object or file path A file object containing the resource. If return_path is True, 'file' will be the full path to the resource. If the file is not found or cannot be opened, None is returned. Description ----------- This function will find a desired resource file and return an opened file object. The main method of finding the resource uses the pkg_resources resource_stream method, which searches your working set for the installed project specified and appends the resource_path given to the project path, leading it to the file. If setuptools is not installed or it cannot find/open the resource, find_resource will use the sys.path[0] to find the resource if alt_path is defined. """ try: # Get the image using the pkg_resources resource_stream module, which # will find the file by getting the Chaco install path and appending the # image path. This method works in all cases as long as setuptools is # installed. If setuptools isn't installed, the backup sys.path[0] # method is used. from pkg_resources import resource_stream, working_set, Requirement # Get a requirement for the project requirement = Requirement.parse(project) if return_path: dist = working_set.find(requirement) full_path = os.path.join(dist.location, resource_path) # If the path exists, return it if os.path.exists(full_path): return full_path else: raise else: return resource_stream(requirement, resource_path) except: # Setuptools was either not installed, or it failed to find the file. # First check to see if the package was installed using egginst by # looking for the file at: site-packages\\resouce_path full_path = os.path.join(get_python_lib(), resource_path) if os.path.exists(full_path): if return_path: return full_path else: return open(full_path, 'rb') # Get the image using sys.path[0], which is the directory that the # running script lives in. The path to the file is then constructed by # navigating from the script's location. This method only works if this # script is called directly from the command line using # 'python %SOMEPATH%/