[
  {
    "path": ".codeclimate.yml",
    "content": "engines:\n  duplication:\n    enabled: true\n    config:\n      languages:\n      - python\n  fixme:\n    enabled: true\n  radon:\n    enabled: true\n  pep8:\n    enabled: true\n  radon: \n    enabled: true\nratings:\n  paths:\n  - \"**.py\"\nexclude_paths: []\n"
  },
  {
    "path": ".gitignore",
    "content": "/.cache\n/*.egg-info\n/*.__pycache__\n/dist\n/build\n*.pyc\n*.coverage\n/.eggs\n*.swp\n/.idea"
  },
  {
    "path": ".landscape.yaml",
    "content": "# https://docs.landscape.io/configuration.html\nstrictness: veryhigh\npython-targets:\n  - 3\npep8:\n    full: true\ndoc-warnings: yes\ntest-warnings: yes\nmax-line-length: 79\nautodetect: yes"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\ncache: pip\nsudo: required\npython:\n- '3.3'\n- '3.4'\n- '3.5'\ninstall:\n# install the package\n- PACKAGE_VERSION=`python setup.py --version`\n- TAG_NAME=v$PACKAGE_VERSION\n# install from the zip file to see if files were forgotten\n- python setup.py sdist --dist-dir=dist --formats=zip\n- ( cd dist ; pip install knittingpattern-${PACKAGE_VERSION}.zip )\n# install the test requirements\n- pip install -r test-requirements.txt\nbefore_script:\n# remove the build folder because it creates problems for py.test\n- rm -rf build\n# show the versions\n- python setup.py --version\n- py.test --version\n- python setup.py requirements\n- echo Package version $PACKAGE_VERSION with possible tag name $TAG_NAME\nscript:\n# test with pep8\n# add coverage for python 3.3 and above\n- py.test --cov=knittingpattern --pep8\n# test import form everywhere\n- ( cd / && python -c \"import knittingpattern;print(\\\"imported\\\")\" )\n# run tests from installation\n- \"( cd / && py.test --pyargs knittingpattern )\"\n# test that the tag represents the version\n# https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables\n- ( if [ -n \"$TRAVIS_TAG\" ]; then if [ $TAG_NAME != $TRAVIS_TAG ]; then echo \"This tag is for the wrong version. Got \\\"$TRAVIS_TAG\\\" expected \\\"$TAG_NAME\\\".\"; exit 1; fi; fi; )\nafter_script:\n# https://github.com/codeclimate/python-test-reporter\n# set the environment variable CODECLIMATE_REPO_TOKEN in travis-ci settings\n- codeclimate-test-reporter\nbefore_deploy:\n# create the documentation\n- ( cd docs ; make html )\n- pip install wheel\n- python setup.py bdist_wheel\ndeploy:\n# created with travis command line tool\n# https://docs.travis-ci.com/user/deployment/pypi\n# $ travis setup pypi\n  provider: pypi\n  user: niccokunzmann2\n  password:\n    secure: nqxnwagFphdLLJsjt4of/jMnLtsMtk8HqoPmENodEfaeue0A4ziIIm46tSBKdBqHURxuCFjj8siuaVCsXiZso/b4aJaIy08C3dcYltiLTfnYDJisicijEMg2wLNffJqiTN2+W6trHFJ7TzYz932jQEMmOC09mynt7LbP7RJOfqOGxvHiVL8D7I677xNLz+Kgu5R5FfZ9lzWcuBEbFTLffFcITeqVM+0yGJv9pZ+rud3RXl3qCAFYsG7SlHnzGuOyV/vWdAmfEuCvW+bs3oFS85Im4LiD1YTE5CQZhrwwGslncjOlWOAlrMuJzWmAG/6OTrIK7nIpI5gVlZdkesZQsx6JeR/22rVjkD9UcKj1R+7lzsC2X9Lh+vMRtxHJnDlW7clUA9+qw+TnvmR85UUhnmaaGtGJwZXDi0TP9wYmg3TaxoKKx5SnYDyFIq5kbVnSxSu1ng0qFMszGH1HYR350fEk8/so3IxdAbrHYbK5xeMv0vISJXdzIv/0U14lb4uB3agWf+SANQkrjYNx4BSE5zP1qj2HC2NsGwXdkl/8HjbTFe9Daj5nLTmGRL80GQ0BpRyJrT5wERRqozuWM1Jb1v6kgADhZhWAUryFlTZ+875He1CYXXoVSI59IER1ccK89NautsrF3mW/4o/WXCTzPDHtDkdavAvVPJc34oTmZgI=\n  on:\n    tags: true\n    distributions: sdist bdist_wheel\n    repo: fossasia/knittingpattern\n"
  },
  {
    "path": "CONTRIBUTING.rst",
    "content": "How to Contribute\n=================\n\n1. Read and agree to the `Developer Certificate of Origin\n<DeveloperCertificateOfOrigin.txt>`_.\n"
  },
  {
    "path": "DeveloperCertificateOfOrigin.txt",
    "content": "Developer Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n660 York Street, Suite 102,\nSan Francisco, CA 94110 USA\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n"
  },
  {
    "path": "LICENSE",
    "content": "GNU LESSER GENERAL PUBLIC LICENSE\n\nVersion 3, 29 June 2007\n\nCopyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>\n\nEveryone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\n\nThis version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.\n0. Additional Definitions.\n\nAs used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License.\n\n“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.\n\nAn “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.\n\nA “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”.\n\nThe “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.\n\nThe “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.\n1. Exception to Section 3 of the GNU GPL.\n\nYou may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.\n2. Conveying Modified Versions.\n\nIf you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:\n\n    a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or\n    b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.\n\n3. Object Code Incorporating Material from Library Header Files.\n\nThe object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:\n\n    a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.\n    b) Accompany the object code with a copy of the GNU GPL and this license document.\n\n4. Combined Works.\n\nYou may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:\n\n    a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.\n    b) Accompany the Combined Work with a copy of the GNU GPL and this license document.\n    c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.\n    d) Do one of the following:\n        0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.\n        1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.\n    e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)\n\n5. Combined Libraries.\n\nYou may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:\n\n    a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.\n    b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.\n\n6. Revised Versions of the GNU Lesser General Public License.\n\nThe Free Software Foundation may publish revised and/or new versions of the GNU 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.\n\nEach version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.\n\nIf the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "recursive-include knittingpattern *.py *.json *.svg\ninclude *requirements.txt\ninclude LICENSE\ninclude knittingpattern/convert/test/pictures/*"
  },
  {
    "path": "README.rst",
    "content": "Knitting Pattern Library\n===============\n\nThe knitting pattern library enables tailors, artisans and home knitters to use a common pattern for knitting machines and hand made knits.\n\n.. image:: https://travis-ci.org/fossasia/knittingpattern.svg\n   :target: https://travis-ci.org/fossasia/knittingpattern\n   :alt: Build Status\n   \n.. image:: https://ci.appveyor.com/api/projects/status/c1983ovsc8thlhvi?svg=true\n   :target: https://ci.appveyor.com/project/AllYarnsAreBeautiful/knittingpattern\n   :alt: AppVeyor CI build status (Windows)\n   \n.. image:: https://codeclimate.com/github/fossasia/knittingpattern/badges/gpa.svg\n   :target: https://codeclimate.com/github/fossasia/knittingpattern\n   :alt: Code Climate\n\n.. image:: https://codeclimate.com/github/fossasia/knittingpattern/badges/coverage.svg\n   :target: https://codeclimate.com/github/fossasia/knittingpattern/coverage\n   :alt: Test Coverage\n\n.. image:: https://codeclimate.com/github/fossasia/knittingpattern/badges/issue_count.svg\n   :target: https://codeclimate.com/github/fossasia/knittingpattern\n   :alt: Issue Count\n\n.. image:: https://badge.fury.io/py/knittingpattern.svg\n   :target: https://pypi.python.org/pypi/knittingpattern\n   :alt: Issue Count\n   \n.. image:: https://img.shields.io/pypi/dm/knittingpattern.svg\n   :target: https://pypi.python.org/pypi/knittingpattern#downloads\n   :alt: Downloads from pypi   \n\n.. image:: https://readthedocs.org/projects/knittingpattern/badge/?version=latest\n   :target: https://knittingpattern.readthedocs.org\n   :alt: Read the Documentation\n\n.. image:: https://landscape.io/github/fossasia/knittingpattern/master/landscape.svg?style=flat\n   :target: https://landscape.io/github/fossasia/knittingpattern/master\n   :alt: Code Health\n\n.. image:: https://badge.waffle.io/fossasia/knittingpattern.svg?label=ready&title=issues%20ready\n   :target: https://waffle.io/fossasia/knittingpattern\n   :alt: Issues ready to work on\n\n\nInstallation and Documentation\n===============\nFor installation instructions and more, `see the documentation\n<http://knittingpattern.readthedocs.io/>`__.\n"
  },
  {
    "path": "appveyor.yml",
    "content": "# see https://packaging.python.org/appveyor/#adding-appveyor-support-to-your-project\nclone_depth: 1\nenvironment:\n\n  PYPI_PASSWORD:\n    secure: Gxrd9WI60wyczr9mHtiQHvJ45Oq0UyQZNrvUtKs2D5w=\n  PYPI_USERNAME: niccokunzmann3\n\n  matrix:\n\n    # For Python versions available on Appveyor, see\n    # http://www.appveyor.com/docs/installed-software#python\n    # The list here is complete (excluding Python 2.6, which\n    # isn't covered by this document) at the time of writing.\n\n    - PYTHON: \"C:\\\\Python33\"\n      UPLOAD_TO_PYPI: true\n    - PYTHON: \"C:\\\\Python34\"\n      UPLOAD_TO_PYPI: false\n    - PYTHON: \"C:\\\\Python35\"\n      UPLOAD_TO_PYPI: false\n# 64 bit does not make a difference\n#    - PYTHON: \"C:\\\\Python33-x64\"\n#      DISTUTILS_USE_SDK: \"1\"\n#    - PYTHON: \"C:\\\\Python34-x64\"\n#      DISTUTILS_USE_SDK: \"1\"\n#    - PYTHON: \"C:\\\\Python35-x64\"\n\ninstall:\n  # We need wheel installed to build wheels\n  - \"%PYTHON%\\\\python.exe -m pip install wheel\"\n\nbuild: off\n\ntest_script:\n  # Put your test command here.\n  # If you don't need to build C extensions on 64-bit Python 3.3 or 3.4,\n  # you can remove \"build.cmd\" from the front of the command, as it's\n  # only needed to support those cases.\n  # Note that you must use the environment variable %PYTHON% to refer to\n  # the interpreter you're using - Appveyor does not do anything special\n  # to put the Python evrsion you want to use on PATH.\n  - \"%PYTHON%\\\\python.exe setup.py test\"\n\nafter_test:\n  # This step builds your wheels.\n  # Again, you only need build.cmd if you're building C extensions for\n  # 64-bit Python 3.3/3.4. And you need to use %PYTHON% to get the correct\n  # interpreter\n  - \"%PYTHON%\\\\python.exe setup.py bdist_wheel\"\n\nartifacts:\n  # bdist_wheel puts your built wheel in the dist directory\n  - path: dist\\*\n\non_success:\n#  You can use this step to upload your artifacts to a public website.\n#  See Appveyor's documentation for more details. Or you can simply\n#  access your wheels from the Appveyor \"artifacts\" tab for your build.\n  - echo \"%APPVEYOR_REPO_TAG%\"\n  - echo \"%APPVEYOR_REPO_TAG_NAME%\"\n  - echo \"%UPLOAD_TO_PYPI%\"\n  - set HOME=.\n  # in https://ci.appveyor.com/project/niccokunzmann/knittingpattern/settings/environment\n  # set the variables for the python package index http://pypi.python.org/\n  #   PYPI_USERNAME\n  #   PYPI_PASSWORD\n  - \"IF %APPVEYOR_REPO_TAG% == true ( %PYTHON%\\\\python.exe -c \\\"import os;print('[distutils]\\\\r\\\\nindex-servers =\\\\r\\\\n    pypi\\\\r\\\\n\\\\r\\\\n[pypi]\\\\r\\\\nusername:{PYPI_USERNAME}\\\\r\\\\npassword:{PYPI_PASSWORD}\\\\r\\\\n'.format(**os.environ))\\\" > %HOME%\\\\.pypirc )\"\n  # upload to pypi\n  # check for the tags\n  # see http://www.appveyor.com/docs/branches#build-on-tags-github-and-gitlab-only\n  - \"IF %APPVEYOR_REPO_TAG% == true ( if \\\"%UPLOAD_TO_PYPI%\\\" == \\\"true\\\" ( FOR /F %%V IN ('%PYTHON%\\\\python.exe setup.py --version') DO ( IF \\\"v%%V\\\" == \\\"%APPVEYOR_REPO_TAG_NAME%\\\" ( %PYTHON%\\\\python.exe setup.py bdist_wininst upload || echo \\\"Error because the build is already uploaded.\\\" ) ELSE ( echo \\\"Invalid tag %APPVEYOR_REPO_TAG_NAME% should be v%%V.\\\" ) ) ) ELSE ( echo \\\"Upload skipped.\\\" ) ) ELSE ( echo \\\"Normal build without PyPi deployment.\\\" )\"\n\n"
  },
  {
    "path": "dev-requirements.in",
    "content": "pip-tools\nSphinx-PyPI-upload3\nautopep8\n"
  },
  {
    "path": "dev-requirements.txt",
    "content": "#\n# This file is autogenerated by pip-compile\n# To update, run:\n#\n#    pip-compile --output-file dev-requirements.txt dev-requirements.in\n#\nautopep8==1.2.4\nclick==6.6                # via pip-tools\nfirst==2.0.1              # via pip-tools\npep8==1.7.0               # via autopep8\npip-tools==1.6.5\nsix==1.10.0               # via pip-tools\nsphinx-pypi-upload3==0.2.2\n"
  },
  {
    "path": "docs/DevelopmentSetup.rst",
    "content": ".. _development-setup:\n\nDevelopment Setup\n=================\n\nMake sure that you have the :ref:`repository installed\n<installation-repository>`.\n\n.. _development-setup-requirements:\n\nInstall Requirements\n--------------------\n\nTo install all requirements for the development setup, execute\n\n.. code:: bash\n\n    pip install --upgrade -r requirements.txt -r test-requirements.txt -r dev-requirements.txt\n\nSphinx Documentation Setup\n--------------------------\n\nSphinx was setup using `the tutorial from readthedocs\n<http://read-the-docs.readthedocs.io/en/latest/getting_started.html>`__.\nIt should be already setup if you completed :ref:`the previous step\n<development-setup-requirements>`.\n\nFurther reading:\n\n- `domains <http://www.sphinx-doc.org/en/stable/domains.html>`__\n\nWith Notepad++ under Windows, you can run the `make_html.bat\n<https://github.com/fossasia/knittingpattern/blob/master/docs/make_html.bat>`__ file in the\n``docs`` directory to create the documentation and show undocumented code.\n\nCode Climate\n------------\n\nTo install the code climate command line interface (cli), read about it in\ntheir github `repository <https://github.com/codeclimate/codeclimate>`__\nYou need docker to be installed. Under Linux you can execute this in the \nTerminal to install docker:\n\n.. code:: bash\n    \n    wget -qO- https://get.docker.com/ | sh\n    sudo usermod -aG docker $USER\n    \nThen, log in and out. Then, you can install the command line interface:\n\n.. code:: bash\n\n    wget -qO- https://github.com/codeclimate/codeclimate/archive/master.tar.gz | tar xvz\n    cd codeclimate-* && sudo make install\n\nThen, go to the knittingpattern repository and analyze it.\n\n.. code:: bash\n\n    codeclimate analyze\n    \nVersion Pinning\n---------------\n\nWe use version pinning, described in `this blog post (outdated)\n<http://nvie.com/posts/pin-your-packages/>`__.\nAlso read the `current version\n<https://github.com/nvie/pip-tools>`__ for how to set up.\n\nAfter installation you can run\n\n.. code:: bash\n\n    pip install -r requirements.in -r test-requirements.in -r dev-requirements.in\n    pip-compile --output-file requirements.txt requirements.in\n    pip-compile --output-file test-requirements.txt test-requirements.in\n    pip-compile --output-file dev-requirements.txt dev-requirements.in\n    pip-sync requirements.txt dev-requirements.txt test-requirements.txt\n    pip install --upgrade -r requirements.txt -r test-requirements.txt -r dev-requirements.txt\n\n``pip-sync`` uninstalls every package you do not need and \nwrites the fix package versions to the requirements files.\n\nContinuous Integration to Pypi\n------------------------------\n\nBefore you put something on `Pypi\n<https://pypi.python.org/pypi/knittingpattern>`__, ensure the following:\n\n1. The version is in the master branch on github.\n2. The tests run by travis-ci run successfully.\n\nPypi is automatically deployed by travis. `See here\n<https://docs.travis-ci.com/user/deployment/pypi>`__.\nTo upload new versions, tag them with git and push them.\n\n.. code:: bash\n\n  setup.py tag_and_deploy\n\nThe tag shows up as a `travis build\n<https://travis-ci.org/fossasia/knittingpattern/builds>`__.\nIf the build succeeds, it is automatically deployed to `Pypi\n<https://pypi.python.org/pypi/knittingpattern>`__.\n\nManual Upload to the Python Package Index\n-----------------------------------------\n\n\nHowever, here you can see how to upload this package manually.\n\nVersion\n~~~~~~~\n\nThroughout this chapter, ``<new_version>`` refers to a a string of the form ``[0-9]+\\.[0-9]+\\.[0-9]+[ab]?`` or ``<MAYOR>.<MINOR>.<STEP>[<MATURITY>]`` where ``<MAYOR>``, ``<MINOR>`` and, ``<STEP>`` represent numbers and ``<MATURITY>`` can be a letter to indicate how mature the release is.\n\n1. Create a new branch for the version.\n\n.. code:: bash\n\n  git checkout -b <new_version>\n\n2. Increase the ``__version__`` in `__init__.py <knittingpattern/__init__.py#L3>`__\n\n   - no letter at the end means release\n   - ``b`` in the end means Beta\n   - ``a`` in the end means Alpha\n\n3. Commit and upload this version.\n\n.. _commit:\n\n.. code:: bash\n  \n  git add knittingpattern/__init__.py\n  git commit -m \"version <new_version>\"\n  git push origin <new_version>\n\n4. Create a pull-request.\n\n5. Wait for `travis-ci <https://travis-ci.org/fossasia/knittingpattern>`__ to pass the tests.\n\n6. Merge the pull-request.\n7. Checkout the master branch and pull the changes from the commit_.\n\n.. code:: bash\n\n  git checkout master\n  git pull\n\n8. Tag the version at the master branch with a ``v`` in the beginning and push it to github.\n\n.. code:: bash\n\n  git tag v<new_version>\n  git push origin v<new_version>\n\n9. Upload_ the code to Pypi.\n  \n\nUpload\n~~~~~~\n\n.. Upload:\n\nFirst ensure all tests are running:\n\n.. code:: bash\n\n    setup.py pep8\n\n\nFrom `docs.python.org\n<https://docs.python.org/3.1/distutils/uploading.html>`__:\n\n.. code:: bash\n\n    setup.py sdist bdist_wininst upload register\n    \nClassifiers\n-----------\n\nYou can find all Pypi classifiers `here\n<http://pypi.python.org/pypi?%3Aaction=list_classifiers>`_.\n\n\n"
  },
  {
    "path": "docs/FileFormatSpecification.rst",
    "content": ".. _FileFormatSpecification:\n\nKnitting Pattern File Format Specification\n==========================================\n\nFor the words see `the glossary\n<https://github.com/AllYarnsAreBeautiful/ayab-desktop/wiki/Glossary>`__.\n\nDesign Decisions\n----------------\n\nConcerns:\n\n- We can never implement everything that is possible with knitting. We must therefore allow instructions to be arbitrary.\n- We can not use a grid as a basis. This does not reflect if you split the work and make i.e. two big legs\n- Knitting can be done on the right and on the wrong side. The same result can be achived when knitting in both directions. \n\nAssumptions\n-----------\n\n- we start from bottom right\n- default instruction (`see\n  <https://github.com/AllYarnsAreBeautiful/ayab-desktop/wiki/2016-05-25---Knitting-pattern>`_)\n  \n  .. code:: json\n    \n    {\n      \"type\" : \"knit\", \n    }\n    {\n      \"type\" : \"ktog tbl\", # identifier\n      \"count\" : 2\n    }\n    \n- default connection\n\n  .. code:: json\n   \n      {\n        \"start\" : 0,\n      }\n        \n- ``\"id\"`` can point to an object.\n\n"
  },
  {
    "path": "docs/Installation.rst",
    "content": ".. _installation:\n\nknittingpattern Installation Instructions\n=========================================\n\nPackage installation from Pypi\n------------------------------\n\nThe knittingpattern library requires `Python 3 <https://www.python.org/>`__.\nIt can be installed form the `Python Package Index\n<https://pypi.python.org/pypi/knittingpattern>`__.\n\nWindows\n~~~~~~~\n\nInstall it with a specific python version under windows:\n\n.. code:: bash\n\n    py -3 -m pip --no-cache-dir install --upgrade knittingpattern\n\nTest the installed version:\n\n.. code:: bash\n\n    py -3 -m pytest --pyargs knittingpattern\n\nLinux\n~~~~~ \n\nTo install the version from the python package index, you can use your terminal and execute this under Linux:\n\n.. code:: shell\n  \n  sudo python3 -m pip --no-cache-dir install --upgrade knittingpattern\n\ntest the installed version:\n\n.. code:: shell\n  \n  python3 -m pytest --pyargs knittingpattern\n\n.. _installation-repository:\n\nInstallation from Repository\n----------------------------\n\nYou can setup the development version under Windows and Linux.\n\n.. _installation-repository-linux:\n\nLinux\n~~~~~\n\nIf you wish to get latest source version running, you can check out the repository and install it manually.\n\n.. code:: bash\n\n  git clone https://github.com/fossasia/knittingpattern.git\n  cd knittingpattern\n  sudo python3 -m pip install --upgrade pip\n  sudo python3 -m pip install -r requirements.txt\n  sudo python3 -m pip install -r test-requirements.txt\n  py.test\n\nTo also make it importable for other libraries, you can link it into the site-packages folder this way:\n\n.. code:: bash\n\n  sudo python3 setup.py link\n\n.. _installation-repository-windows:\n\nWindows\n~~~~~~~\n\nSame as under :ref:`installation-repository-linux` but you need to replace\n``sudo python3`` with ``py -3``. This also counts for the following\ndocumentation.\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = ../build\nDOCDIR        = .\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(DOCDIR)\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(DOCDIR)\n\n.PHONY: help\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  applehelp  to make an Apple Help Book\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  epub3      to make an epub3\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\t@echo \"  coverage   to run coverage check of the documentation (if enabled)\"\n\t@echo \"  dummy      to check syntax errors of document sources\"\n\n.PHONY: clean\nclean:\n\trm -rf $(BUILDDIR)/*\n\n.PHONY: html\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\n.PHONY: dirhtml\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\n.PHONY: singlehtml\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\n.PHONY: pickle\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\n.PHONY: json\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\n.PHONY: htmlhelp\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\n.PHONY: qthelp\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/knittingpattern.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/knittingpattern.qhc\"\n\n.PHONY: applehelp\napplehelp:\n\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp\n\t@echo\n\t@echo \"Build finished. The help book is in $(BUILDDIR)/applehelp.\"\n\t@echo \"N.B. You won't be able to view it unless you put it in\" \\\n\t      \"~/Library/Documentation/Help or install it in your application\" \\\n\t      \"bundle.\"\n\n.PHONY: devhelp\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/knittingpattern\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/knittingpattern\"\n\t@echo \"# devhelp\"\n\n.PHONY: epub\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\n.PHONY: epub3\nepub3:\n\t$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3\n\t@echo\n\t@echo \"Build finished. The epub3 file is in $(BUILDDIR)/epub3.\"\n\n.PHONY: latex\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\n.PHONY: latexpdf\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: latexpdfja\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: text\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\n.PHONY: man\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\n.PHONY: texinfo\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\n.PHONY: info\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\n.PHONY: gettext\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\n.PHONY: changes\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\n.PHONY: linkcheck\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\n.PHONY: doctest\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\n.PHONY: coverage\ncoverage:\n\t$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage\n\t@echo \"Testing of coverage in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/coverage/python.txt.\"\n\n.PHONY: xml\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\n.PHONY: pseudoxml\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n\n.PHONY: dummy\ndummy:\n\t$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy\n\t@echo\n\t@echo \"Build finished. Dummy builder generates no files.\"\n"
  },
  {
    "path": "docs/_static/.gitignore",
    "content": "# This file is used so the folder turns up in git."
  },
  {
    "path": "docs/_templates/.gitignore",
    "content": "# This file is used so the folder turns up in git."
  },
  {
    "path": "docs/conf.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# knittingpattern documentation build configuration file, created by\n# sphinx-quickstart on Thu Jun 23 09:49:51 2016.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\nHERE = os.path.dirname(__file__)\nsys.path.insert(0, os.path.abspath(os.path.join(HERE, '..')))\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.doctest',\n    'sphinx.ext.intersphinx',\n    'sphinx.ext.todo',\n    'sphinx.ext.coverage',\n    'sphinx.ext.mathjax',\n    'sphinx.ext.ifconfig',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.githubpages',\n    'sphinx_paramlinks',  # https://pypi.python.org/pypi/sphinx-paramlinks/\n                          # http://stackoverflow.com/a/20845306/1320237\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#\n# source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'knittingpattern'\ncopyright = '2016, AllYarnsAreBeautiful & FOSSASIA'\nauthor = 'AllYarnsAreBeautiful & FOSSASIA'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\n# version = '0.0'\n# The full version, including alpha/beta/rc tags.\n# release = '8'\nproject_version = __import__(project).__version__\nversion, release = project_version.rsplit(\".\", 1)\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#\n# today = ''\n#\n# Else, today_fmt is used as the format for a strftime call.\n#\n# today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = []\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n#\n# default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#\nadd_function_parentheses = True\n\n# http://stackoverflow.com/q/5599254/1320237\n# use the doc string in the init method\n# autoclass_content = 'both'\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#\n# add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#\n# show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n# modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n# keep_warnings = False\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = True\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\n\n# from https://github.com/rtfd/readthedocs.org/blob/master/docs/conf.py\non_rtd = os.environ.get('READTHEDOCS', None) == 'True'\nif not on_rtd:  # only import and set the theme if we're building docs locally\n    import sphinx_rtd_theme\n    html_theme = 'sphinx_rtd_theme'\n    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n# html_theme_path = []\n\n# The name for this set of Sphinx documents.\n# \"<project> v<release> documentation\" by default.\n#\nhtml_title = project + \" \" + project_version\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#\nhtml_short_title = project\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#\n# html_logo = None\n# TODO\n\n# The name of an image file (relative to this directory) to use as a favicon of\n# the docs.  This file should be a Windows icon file (.ico) being 16x16 or\n# 32x32 pixels large.\n#\n# html_favicon = None\n# TODO\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n#\n# html_extra_path = []\n\n# If not None, a 'Last updated on:' timestamp is inserted at every page\n# bottom, using the given strftime format.\n# The empty string is equivalent to '%b %d, %Y'.\n#\n# html_last_updated_fmt = None\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#\n# html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#\n# html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#\n# html_additional_pages = {}\n\n# If false, no module index is generated.\n#\n# html_domain_indices = True\n\n# If false, no index is generated.\n#\n# html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#\n# html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#\nhtml_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#\n# html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#\nhtml_show_copyright = False\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#\n# html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n# html_file_suffix = None\n\n# Language to be used for generating the HTML full-text search index.\n# Sphinx supports the following languages:\n#   'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'\n#   'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'\n#\nhtml_search_language = 'en'\n\n# A dictionary with options for the search language support, empty by default.\n# 'ja' uses this config value.\n# 'zh' user can custom change `jieba` dictionary path.\n#\n# html_search_options = {'type': 'default'}\n\n# The name of a javascript file (relative to the configuration directory) that\n# implements a search results scorer. If empty, the default will be used.\n#\n# html_search_scorer = 'scorer.js'\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'knittingpatterndoc'\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n     # The paper size ('letterpaper' or 'a4paper').\n     #\n     # 'papersize': 'letterpaper',\n\n     # The font size ('10pt', '11pt' or '12pt').\n     #\n     # 'pointsize': '10pt',\n\n     # Additional stuff for the LaTeX preamble.\n     #\n     # 'preamble': '',\n\n     # Latex figure (float) alignment\n     #\n     # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'knittingpattern.tex', 'knittingpattern Documentation',\n     'AllYarnsAreBeautiful & FOSSASIA', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#\n# latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#\n# latex_use_parts = False\n\n# If true, show page references after internal links.\n#\n# latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#\n# latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n#\n# latex_appendices = []\n\n# If false, no module index is generated.\n#\n# latex_domain_indices = True\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, project, project + ' Documentation',\n     [author], 1)\n]\n\n# If true, show URL addresses after external links.\n#\n# man_show_urls = False\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, project, project + ' Documentation',\n     author, project, 'One line description of project.',  # TODO\n     'Miscellaneous'),\n]\n\n# Documents to append as an appendix to all manuals.\n#\n# texinfo_appendices = []\n\n# If false, no module index is generated.\n#\n# texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#\ntexinfo_show_urls = 'footnote'\n\n# If true, do not generate a @detailmenu in the \"Top\" node's menu.\n#\n# texinfo_no_detailmenu = False\n\n\n# -- Options for Epub output ----------------------------------------------\n\n# Bibliographic Dublin Core info.\nepub_title = project\nepub_author = author\nepub_publisher = author\nepub_copyright = copyright\n\n# The basename for the epub file. It defaults to the project name.\n# epub_basename = project\n\n# The HTML theme for the epub output. Since the default themes are not\n# optimized for small screen space, using the same theme for HTML and epub\n# output is usually not wise. This defaults to 'epub', a theme designed to save\n# visual space.\n#\n# epub_theme = 'epub'\n\n# The language of the text. It defaults to the language option\n# or 'en' if the language is not set.\n#\n# epub_language = ''\n\n# The scheme of the identifier. Typical schemes are ISBN or URL.\n# epub_scheme = ''\n\n# The unique identifier of the text. This can be a ISBN number\n# or the project homepage.\n#\n# epub_identifier = ''\n\n# A unique identification for the text.\n#\n# epub_uid = ''\n\n# A tuple containing the cover image and cover page html template filenames.\n#\n# epub_cover = ()\n\n# A sequence of (type, uri, title) tuples for the guide element of content.opf.\n#\n# epub_guide = ()\n\n# HTML files that should be inserted before the pages created by sphinx.\n# The format is a list of tuples containing the path and title.\n#\n# epub_pre_files = []\n\n# HTML files that should be inserted after the pages created by sphinx.\n# The format is a list of tuples containing the path and title.\n#\n# epub_post_files = []\n\n# A list of files that should not be packed into the epub file.\nepub_exclude_files = ['search.html']\n\n# The depth of the table of contents in toc.ncx.\n#\n# epub_tocdepth = 3\n\n# Allow duplicate toc entries.\n#\n# epub_tocdup = True\n\n# Choose between 'default' and 'includehidden'.\n#\n# epub_tocscope = 'default'\n\n# Fix unsupported image types using the Pillow.\n#\n# epub_fix_images = False\n\n# Scale large images.\n#\n# epub_max_image_width = 0\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#\n# epub_show_urls = 'inline'\n\n# If false, no index is generated.\n#\n# epub_use_index = True\n\n\n# Example configuration for intersphinx: refer to the Python standard library.\nintersphinx_mapping = {\n    \"https://docs.python.org/3/\": None,\n    \"http://observablelist.readthedocs.io/en/latest/\": None,\n    \"https://kivy.org/docs/\": None}\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. knittingpattern documentation master file, created by\n   sphinx-quickstart on Thu Jun 23 09:49:51 2016.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nWelcome to knittingpattern's documentation!\n===========================================\n\nContents:\n\n.. toctree::\n   :maxdepth: 2\n\n   Installation\n   FileFormatSpecification\n   DevelopmentSetup\n   reference/index\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nset DOCDIR=.\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUILDDIR=../build\nset ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %DOCDIR%\nset I18NSPHINXOPTS=%SPHINXOPTS% %DOCDIR%\nif NOT \"%PAPER%\" == \"\" (\n\tset ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%\n\tset I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%\n)\n\nif \"%1\" == \"\" goto help\n\nif \"%1\" == \"help\" (\n\t:help\n\techo.Please use `make ^<target^>` where ^<target^> is one of\n\techo.  html       to make standalone HTML files\n\techo.  dirhtml    to make HTML files named index.html in directories\n\techo.  singlehtml to make a single large HTML file\n\techo.  pickle     to make pickle files\n\techo.  json       to make JSON files\n\techo.  htmlhelp   to make HTML files and a HTML help project\n\techo.  qthelp     to make HTML files and a qthelp project\n\techo.  devhelp    to make HTML files and a Devhelp project\n\techo.  epub       to make an epub\n\techo.  epub3      to make an epub3\n\techo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\n\techo.  text       to make text files\n\techo.  man        to make manual pages\n\techo.  texinfo    to make Texinfo files\n\techo.  gettext    to make PO message catalogs\n\techo.  changes    to make an overview over all changed/added/deprecated items\n\techo.  xml        to make Docutils-native XML files\n\techo.  pseudoxml  to make pseudoxml-XML files for display purposes\n\techo.  linkcheck  to check all external links for integrity\n\techo.  doctest    to run all doctests embedded in the documentation if enabled\n\techo.  coverage   to run coverage check of the documentation if enabled\n\techo.  dummy      to check syntax errors of document sources\n\tgoto end\n)\n\nif \"%1\" == \"clean\" (\n\tfor /d %%i in (%BUILDDIR%\\*) do rmdir /q /s %%i\n\tdel /q /s %BUILDDIR%\\*\n\tgoto end\n)\n\n\nREM Check if sphinx-build is available and fallback to Python version if any\n%SPHINXBUILD% 1>NUL 2>NUL\nif errorlevel 9009 goto sphinx_python\ngoto sphinx_ok\n\n:sphinx_python\n\nset SPHINXBUILD=python -m sphinx.__init__\n%SPHINXBUILD% 2> nul\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n:sphinx_ok\n\n\nif \"%1\" == \"html\" (\n\t%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/html.\n\tgoto end\n)\n\nif \"%1\" == \"dirhtml\" (\n\t%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.\n\tgoto end\n)\n\nif \"%1\" == \"singlehtml\" (\n\t%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.\n\tgoto end\n)\n\nif \"%1\" == \"pickle\" (\n\t%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can process the pickle files.\n\tgoto end\n)\n\nif \"%1\" == \"json\" (\n\t%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can process the JSON files.\n\tgoto end\n)\n\nif \"%1\" == \"htmlhelp\" (\n\t%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can run HTML Help Workshop with the ^\n.hhp project file in %BUILDDIR%/htmlhelp.\n\tgoto end\n)\n\nif \"%1\" == \"qthelp\" (\n\t%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can run \"qcollectiongenerator\" with the ^\n.qhcp project file in %BUILDDIR%/qthelp, like this:\n\techo.^> qcollectiongenerator %BUILDDIR%\\qthelp\\knittingpattern.qhcp\n\techo.To view the help file:\n\techo.^> assistant -collectionFile %BUILDDIR%\\qthelp\\knittingpattern.ghc\n\tgoto end\n)\n\nif \"%1\" == \"devhelp\" (\n\t%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished.\n\tgoto end\n)\n\nif \"%1\" == \"epub\" (\n\t%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The epub file is in %BUILDDIR%/epub.\n\tgoto end\n)\n\nif \"%1\" == \"epub3\" (\n\t%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The epub3 file is in %BUILDDIR%/epub3.\n\tgoto end\n)\n\nif \"%1\" == \"latex\" (\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; the LaTeX files are in %BUILDDIR%/latex.\n\tgoto end\n)\n\nif \"%1\" == \"latexpdf\" (\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\n\tcd %BUILDDIR%/latex\n\tmake all-pdf\n\tcd %~dp0\n\techo.\n\techo.Build finished; the PDF files are in %BUILDDIR%/latex.\n\tgoto end\n)\n\nif \"%1\" == \"latexpdfja\" (\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\n\tcd %BUILDDIR%/latex\n\tmake all-pdf-ja\n\tcd %~dp0\n\techo.\n\techo.Build finished; the PDF files are in %BUILDDIR%/latex.\n\tgoto end\n)\n\nif \"%1\" == \"text\" (\n\t%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The text files are in %BUILDDIR%/text.\n\tgoto end\n)\n\nif \"%1\" == \"man\" (\n\t%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The manual pages are in %BUILDDIR%/man.\n\tgoto end\n)\n\nif \"%1\" == \"texinfo\" (\n\t%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.\n\tgoto end\n)\n\nif \"%1\" == \"gettext\" (\n\t%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The message catalogs are in %BUILDDIR%/locale.\n\tgoto end\n)\n\nif \"%1\" == \"changes\" (\n\t%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.The overview file is in %BUILDDIR%/changes.\n\tgoto end\n)\n\nif \"%1\" == \"linkcheck\" (\n\t%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Link check complete; look for any errors in the above output ^\nor in %BUILDDIR%/linkcheck/output.txt.\n\tgoto end\n)\n\nif \"%1\" == \"doctest\" (\n\t%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Testing of doctests in the sources finished, look at the ^\nresults in %BUILDDIR%/doctest/output.txt.\n\tgoto end\n)\n\nif \"%1\" == \"coverage\" (\n\t%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Testing of coverage in the sources finished, look at the ^\nresults in %BUILDDIR%/coverage/python.txt.\n\tgoto end\n)\n\nif \"%1\" == \"xml\" (\n\t%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The XML files are in %BUILDDIR%/xml.\n\tgoto end\n)\n\nif \"%1\" == \"pseudoxml\" (\n\t%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.\n\tgoto end\n)\n\nif \"%1\" == \"dummy\" (\n\t%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. Dummy builder generates no files.\n\tgoto end\n)\n\n:end\n"
  },
  {
    "path": "docs/make_html.bat",
    "content": "@echo off\n\nREM \nREM This is a shortcut for notepad++\nREM You can press F5 and use this as command to update the html of the docs. \nREM \n\ncd \"%~dp0\"\n\ncall make html\ncall make coverage\n\npy -c \"print(open('../build/coverage/Python.txt').read())\"\n\npy -c \"import time;time.sleep(10);print('exit')\""
  },
  {
    "path": "docs/reference/index.rst",
    "content": "Reference\n=========\n\n.. toctree::\n   :maxdepth: 2\n\n   knittingpattern/index\n   knittingpattern/convert/index\n   knittingpattern/Dumper/index\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/FileWrapper.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Dumper.FileWrapper\n\n:py:mod:`FileWrapper` Module\n============================\n\n.. automodule:: knittingpattern.Dumper.FileWrapper\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/file.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Dumper.file\n\n:py:mod:`file` Module\n=====================\n\n.. automodule:: knittingpattern.Dumper.file\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/index.rst",
    "content": "The ``knittingpattern.Dumper`` Module Reference\n===============================================\n\n.. toctree::\n   :maxdepth: 2\n   \n   init\n   file\n   FileWrapper\n   json\n   svg\n   xml\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/init.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Dumper\n\n:py:mod:`Dumper` Module\n=======================\n\n.. automodule:: knittingpattern.Dumper\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/json.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Dumper.json\n\n:py:mod:`json` Module\n=====================\n\n.. automodule:: knittingpattern.Dumper.json\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/svg.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Dumper.svg\n\n:py:mod:`svg` Module\n====================\n\n.. automodule:: knittingpattern.Dumper.svg\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Dumper/xml.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Dumper.xml\n\n:py:mod:`xml` Module\n====================\n\n.. automodule:: knittingpattern.Dumper.xml\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/IdCollection.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.IdCollection\n\n:py:mod:`IdCollection` Module\n=============================\n\n.. automodule:: knittingpattern.IdCollection\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Instruction.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Instruction\n\n:py:mod:`Instruction` Module\n============================\n\n.. automodule:: knittingpattern.Instruction\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/InstructionLibrary.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.InstructionLibrary\n\n:py:mod:`InstructionLibrary` Module\n===================================\n\n.. automodule:: knittingpattern.InstructionLibrary\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/KnittingPattern.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.KnittingPattern\n\n:py:mod:`KnittingPattern` Module\n================================\n\n.. automodule:: knittingpattern.KnittingPattern\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/KnittingPatternSet.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.KnittingPatternSet\n\n:py:mod:`KnittingPatternSet` Module\n===================================\n\n.. automodule:: knittingpattern.KnittingPatternSet\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Loader.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Loader\n\n:py:mod:`Loader` Module\n=======================\n\n.. automodule:: knittingpattern.Loader\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Mesh.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Mesh\n\n:py:mod:`Mesh` Module\n=====================\n\n.. automodule:: knittingpattern.Mesh\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Parser.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Parser\n\n:py:mod:`Parser` Module\n=======================\n\n.. automodule:: knittingpattern.Parser\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/ParsingSpecification.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.ParsingSpecification\n\n:py:mod:`ParsingSpecification` Module\n=====================================\n\n.. automodule:: knittingpattern.ParsingSpecification\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Prototype.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Prototype\n\n:py:mod:`Prototype` Module\n==========================\n\n.. automodule:: knittingpattern.Prototype\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/Row.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.Row\n\n:py:mod:`Row` Module\n====================\n\n.. automodule:: knittingpattern.Row\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/AYABPNGBuilder.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.AYABPNGBuilder\n\n:py:mod:`AYABPNGBuilder` Module\n===============================\n\n.. automodule:: knittingpattern.convert.AYABPNGBuilder\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/AYABPNGDumper.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.AYABPNGDumper\n\n:py:mod:`AYABPNGDumper` Module\n==============================\n\n.. automodule:: knittingpattern.convert.AYABPNGDumper\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/InstructionSVGCache.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.InstructionSVGCache\n\n:py:mod:`InstructionSVGCache` Module\n====================================\n\n.. automodule:: knittingpattern.convert.InstructionSVGCache\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/InstructionToSVG.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.InstructionToSVG\n\n:py:mod:`InstructionToSVG` Module\n=================================\n\n.. automodule:: knittingpattern.convert.InstructionToSVG\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/KnittingPatternToSVG.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.KnittingPatternToSVG\n\n:py:mod:`KnittingPatternToSVG` Module\n=====================================\n\n.. automodule:: knittingpattern.convert.KnittingPatternToSVG\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/Layout.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.Layout\n\n:py:mod:`Layout` Module\n=======================\n\n.. automodule:: knittingpattern.convert.Layout\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/SVGBuilder.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.SVGBuilder\n\n:py:mod:`SVGBuilder` Module\n===========================\n\n.. automodule:: knittingpattern.convert.SVGBuilder\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/color.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.color\n\n:py:mod:`color` Module\n======================\n\n.. automodule:: knittingpattern.convert.color\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/image_to_knittingpattern.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.image_to_knittingpattern\n\n:py:mod:`image_to_knittingpattern` Module\n=========================================\n\n.. automodule:: knittingpattern.convert.image_to_knittingpattern\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/index.rst",
    "content": "The ``knittingpattern.convert`` Module Reference\n================================================\n\n.. toctree::\n   :maxdepth: 2\n\n   init\n   color\n   AYABPNGBuilder\n   AYABPNGDumper\n   image_to_knittingpattern\n   InstructionToSVG\n   InstructionSVGCache\n   KnittingPatternToSVG\n   Layout\n   load_and_dump\n   SVGBuilder\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/init.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert\n\n:py:mod:`convert` Module\n========================\n\n.. automodule:: knittingpattern.convert\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/convert/load_and_dump.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.convert.load_and_dump\n\n:py:mod:`load_and_dump` Module\n==============================\n\n.. automodule:: knittingpattern.convert.load_and_dump\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/index.rst",
    "content": "The ``knittingpattern`` Module Reference\n========================================\n\n.. toctree::\n   :maxdepth: 2\n\n   init\n   IdCollection\n   Instruction\n   InstructionLibrary\n   KnittingPattern\n   KnittingPatternSet\n   Loader\n   Mesh\n   Parser\n   ParsingSpecification\n   Prototype\n   Row\n   utils\n   walk\n"
  },
  {
    "path": "docs/reference/knittingpattern/init.rst",
    "content": "\n.. py:currentmodule:: knittingpattern\n\n:py:mod:`knittingpattern` Module\n================================\n\n.. automodule:: knittingpattern\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/utils.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.utils\n\n:py:mod:`utils` Module\n======================\n\n.. automodule:: knittingpattern.utils\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/reference/knittingpattern/walk.rst",
    "content": "\n.. py:currentmodule:: knittingpattern.walk\n\n:py:mod:`walk` Module\n=====================\n\n.. automodule:: knittingpattern.walk\n   :show-inheritance:\n   :members:\n   :special-members:\n\n"
  },
  {
    "path": "docs/test/test_docs.py",
    "content": "import os\n\n\ndef absjoin(*args):\n    \"\"\"\n    :return: an absolute path to the joined arguments\n    :param args: the parts of the path to join\n    \"\"\"\n    return os.path.abspath(os.path.join(*args))\n\nPACKAGE = \"knittingpattern\"\n\nHERE = absjoin(os.path.dirname(__file__))\nDOCS_DIRECTORY = absjoin(HERE, \"..\")\nPACKAGE_LOCATION = absjoin(DOCS_DIRECTORY, \"..\")\nPACKAGE_ROOT = absjoin(PACKAGE_LOCATION, PACKAGE)\nPACKAGE_DOCUMENTATION = absjoin(HERE, \"..\", \"reference\")\nBUILD_DIRECTORY = absjoin(PACKAGE_LOCATION, \"build\")\nCOVERAGE_DIRECTORY = absjoin(BUILD_DIRECTORY, \"coverage\")\nPYTHON_COVERAGE_FILE = absjoin(COVERAGE_DIRECTORY, \"python.txt\")\n\n__all__ = [\"PACKAGE\", \"HERE\", \"DOCS_DIRECTORY\", \"PACKAGE_LOCATION\",\n           \"PACKAGE_ROOT\", \"PACKAGE_DOCUMENTATION\", \"BUILD_DIRECTORY\",\n           \"COVERAGE_DIRECTORY\", \"PYTHON_COVERAGE_FILE\"]\n"
  },
  {
    "path": "docs/test/test_documentation_sources_exist.py",
    "content": "#!/usr/bin/python3\n\"\"\"Test the coverage of documentation.\n\nNo function shall be left out by the documentation.\nRun this module to create the missing documentation files.\n\n\"\"\"\nfrom test_docs import PACKAGE_LOCATION, PACKAGE, PACKAGE_DOCUMENTATION, \\\n    PACKAGE_ROOT\nimport pytest\nfrom collections import namedtuple\nimport os\n\n\ndef relative_module_path(absolute_path):\n    relative_path = absolute_path[len(PACKAGE_LOCATION):]\n    if not relative_path.startswith(PACKAGE):\n        # remove /\n        relative_path = relative_path[1:]\n    assert relative_path.startswith(PACKAGE)\n    return relative_path\n\n\ndef module_name_and_doc(relative_path):\n    assert relative_path.startswith(PACKAGE)\n    file, ext = os.path.splitext(relative_path)\n    assert ext == \".py\"\n    names = []\n    while file:\n        file, name = os.path.split(file)\n        names.insert(0, name)\n    assert names\n    doc = names[:-1] + [names[-1].replace(\"__\", \"\") + \".rst\"]\n    doc_file = os.path.join(PACKAGE_DOCUMENTATION, *doc)\n    if names[-1] == \"__init__\":\n        del names[-1]\n    return \".\".join(names), doc_file\n\n\nModule = namedtuple(\"Module\", [\"absolute_path\", \"path\", \"name\", \"doc_file\",\n                               \"lines\", \"title\"])\nMODULES = []\n\n\ndef add_module(absolute_path):\n    relative_path = relative_module_path(absolute_path)\n    name, doc_path = module_name_and_doc(relative_path)\n    if os.path.isfile(doc_path):\n        with open(doc_path) as file:\n            lines = file.readlines()\n    else:\n        lines = []\n    relative_name = name.rsplit(\".\", 1)[-1]\n    title = \":py:mod:`{}` Module\".format(relative_name)\n    MODULES.append(Module(absolute_path, relative_path, name, doc_path, lines,\n                          title))\n\n\nfor dirpath, dirnames, filenames in os.walk(PACKAGE_ROOT):\n    if \"__init__.py\" not in filenames:\n        # only use module content\n        continue\n    for filename in filenames:\n        if filename.endswith(\".py\"):\n            add_module(os.path.join(dirpath, filename))\n\n\nCREATE_MODULE_MESSAGE = \"You can execute {} to create the missing \"\\\n                        \"documentation file.\".format(__file__)\n\n\n@pytest.mark.parametrize('module', MODULES)\ndef test_module_has_a_documentation_file(module):\n    assert os.path.isfile(module.doc_file), CREATE_MODULE_MESSAGE\n\n\n@pytest.mark.parametrize('module', MODULES)\ndef test_documentation_references_module(module):\n    # assert module.lines[0].strip() == \".. py:module:: \" + module.name\n    assert module.lines[1].strip() == \".. py:currentmodule:: \" + module.name\n\n\n@pytest.mark.parametrize('module', MODULES)\ndef test_documentation_has_proper_title(module):\n    assert module.lines[2].strip() == \"\"\n    assert module.lines[3].strip() == module.title\n    assert module.lines[4].strip() == \"=\" * len(module.title)\n\n\ndef create_new_module_documentation():\n    \"\"\"Create documentation so it fits the tests.\"\"\"\n    for module in MODULES:\n        if not os.path.isfile(module.doc_file):\n            directory = os.path.dirname(module.doc_file)\n            os.makedirs(directory, exist_ok=True)\n            with open(module.doc_file, \"w\") as file:\n                write = file.write\n                write(\"\\n\")  # .. py:module:: \" + module.name + \"\\n\")\n                write(\".. py:currentmodule:: \" + module.name + \"\\n\")\n                write(\"\\n\")\n                write(module.title + \"\\n\")\n                write(\"=\" * len(module.title) + \"\\n\")\n                write(\"\\n\")\n                write(\".. automodule:: \" + module.name + \"\\n\")\n                write(\"   :show-inheritance:\\n\")\n                write(\"   :members:\\n\")\n                write(\"   :special-members:\\n\")\n                write(\"\\n\")\n\n\ncreate_new_module_documentation()\n"
  },
  {
    "path": "docs/test/test_sphinx_build.py",
    "content": "\"\"\"Test the building process of the documentation.\n\n- All modules should be documented.\n- All public methods/classes/functions/constants should be documented\n\"\"\"\nfrom test_docs import BUILD_DIRECTORY, DOCS_DIRECTORY, PYTHON_COVERAGE_FILE\nimport subprocess\nimport re\nimport shutil\nfrom pytest import fixture\nimport os\n\n\nUNDOCUMENTED_PYTHON_OBJECTS = \"\"\"Undocumented Python objects\n===========================\n\"\"\"\nWARNING_PATTERN = b\"(?:checking consistency\\\\.\\\\.\\\\. )?\" \\\n                  b\"(.*(?:WARNING|SEVERE|ERROR):.*)\"\n\n\ndef print_bytes(bytes_):\n    \"\"\"Print bytes safely as string.\"\"\"\n    try:\n        print(bytes_.decode())\n    except UnicodeDecodeError:\n        print(bytes_)\n\n\n@fixture(scope=\"module\")\ndef sphinx_build():\n    \"\"\"Build the documentation with sphinx and return the output.\"\"\"\n    if os.path.exists(BUILD_DIRECTORY):\n        shutil.rmtree(BUILD_DIRECTORY)\n    output = subprocess.check_output(\n            \"make html\", shell=True, cwd=DOCS_DIRECTORY,\n            stderr=subprocess.STDOUT\n        )\n    output += subprocess.check_output(\n            \"make coverage\", shell=True, cwd=DOCS_DIRECTORY,\n            stderr=subprocess.STDOUT\n        )\n    print(output.decode())\n    return output\n\n\n@fixture(scope=\"module\")\ndef coverage(sphinx_build):\n    \"\"\":return: the documentation coverage outpupt.\"\"\"\n    assert sphinx_build, \"we built before we try to access the result\"\n    with open(PYTHON_COVERAGE_FILE) as file:\n        return file.read()\n\n\n@fixture\ndef warnings(sphinx_build):\n    \"\"\":return: the warnings during the build process.\"\"\"\n    return re.findall(WARNING_PATTERN, sphinx_build)\n\n\ndef test_all_methods_are_documented(coverage):\n    \"\"\"Make sure that everything that is public is also documented.\"\"\"\n    print(coverage)\n    assert coverage == UNDOCUMENTED_PYTHON_OBJECTS\n\n\ndef test_doc_build_passes_without_warnings(warnings):\n    \"\"\"Make sure that the documentation is semantically correct.\"\"\"\n    #  WARNING: document isn't included in any toctree\n    for warning in warnings:\n        print_bytes(warning.strip())\n    assert warnings == []\n"
  },
  {
    "path": "knittingpattern/Dumper/FileWrapper.py",
    "content": "\"\"\"This module provides wrappers for file-like objects\nfor encoding and decoding.\n\n\"\"\"\n\n\nclass BytesWrapper(object):\n    \"\"\"Use this class if you have a text-file but you want to\n    write bytes to it.\n    \"\"\"\n\n    def __init__(self, text_file, encoding):\n        \"\"\"Create a wrapper around :paramref:`text_file` that decodes\n        bytes to string using :paramref:`encoding` and writes them\n        to :paramref:`text_file`.\n\n        :param str encoding: The encoding to use to transfer the written bytes\n          to string so they can be written to :paramref:`text_file`\n        :param text_file: a file-like object open in text mode\n        \"\"\"\n        self._file = text_file\n        self._encoding = encoding\n\n    def write(self, bytes_):\n        \"\"\"Write bytes to the file.\"\"\"\n        string = bytes_.decode(self._encoding)\n        self._file.write(string)\n\n\nclass TextWrapper(object):\n    \"\"\"Use this class if you have a binary-file but you want to\n    write strings to it.\n    \"\"\"\n\n    def __init__(self, binary_file, encoding):\n        \"\"\"Create a wrapper around :paramref:`binary_file` that encodes\n        strings to bytes using :paramref:`encoding` and writes them\n        to :paramref:`binary_file`.\n\n        :param str encoding: The encoding to use to transfer the written string\n          to bytes so they can be written to :paramref:`binary_file`\n        :param binary_file: a file-like object open in binary mode\n        \"\"\"\n        self._file = binary_file\n        self._encoding = encoding\n\n    def write(self, string):\n        \"\"\"Write a string to the file.\"\"\"\n        bytes_ = string.encode(self._encoding)\n        self._file.write(bytes_)\n\n\n__all__ = [\"TextWrapper\", \"BytesWrapper\"]\n"
  },
  {
    "path": "knittingpattern/Dumper/__init__.py",
    "content": "\"\"\"Writing objects to files\n\nThis module offers a unified interface to serialize objects to strings\nand save them to files.\n\"\"\"\nfrom .file import ContentDumper as ContentDumper\nfrom .json import JSONDumper as JSONDumper\nfrom .xml import XMLDumper as XMLDumper\nfrom .svg import SVGDumper as SVGDumper\n\n__all__ = [\"ContentDumper\", \"JSONDumper\", \"XMLDumper\", \"SVGDumper\"]\n"
  },
  {
    "path": "knittingpattern/Dumper/file.py",
    "content": "\"\"\"Save strings to files.\"\"\"\nfrom io import StringIO, BytesIO\nfrom tempfile import NamedTemporaryFile\nfrom .FileWrapper import BytesWrapper, TextWrapper\n\n\nclass ContentDumper(object):\n\n    \"\"\"This class is a unified interface for saving objects.\n\n    The idea is to decouple the place to save to from the process used\n    to dump the content.\n    We are saving several objects such as patterns and SVGs.\n    They should all have the same convenient interface.\n\n    The process of saving something usually requires writing to some file.\n    However, users may want to have the result as a string, an open file,\n    a file on the hard drive on a fixed or temporary location,\n    posted to some url or in a zip file.\n    This class should provide for all those needs while providing a uniform\n    interface for the dumping.\n    \"\"\"\n\n    def __init__(self, on_dump, text_is_expected=True, encoding=\"UTF-8\"):\n        \"\"\"Create a new dumper object with a function :paramref:`on_dump`\n\n        :param on_dump: a function that takes a file-like object as argument\n          and writes content to it.\n        :param bool text_is_expected: whether to use text mode\n          (:obj:`True`, default) or binary mode (:obj:`False`)\n          for :paramref:`on_dump`.\n\n        The dumper calls :paramref:`on_dump` with a file-like object every time\n        one of its save methods, e.g. :meth:`string` or :meth:`file` is called.\n        The file-like object in the :paramref:`file` argument supports the\n        method ``write()`` to which the content should be written.\n\n        :paramref:`text_is_expected` should be\n\n        - :obj:`True` to pass a file to :paramref:`on_dump` that you can write\n          strings to\n\n        - :obj:`False` to pass a file to :paramref:`on_dump` that you can write\n          bytes to\n        \"\"\"\n        self.__dump_to_file = on_dump\n        self.__text_is_expected = text_is_expected\n        self.__encoding = encoding\n\n    @property\n    def encoding(self):\n        \"\"\":return: the encoding for byte to string conversion\n        :rtype: str\"\"\"\n        return self.__encoding\n\n    def string(self):\n        \"\"\":return: the dump as a string\"\"\"\n        if self.__text_is_expected:\n            return self._string()\n        else:\n            return self._bytes().decode(self.__encoding)\n\n    def _string(self):\n        \"\"\":return: the string from a :class:`io.StringIO`\"\"\"\n        file = StringIO()\n        self.__dump_to_file(file)\n        file.seek(0)\n        return file.read()\n\n    def bytes(self):\n        \"\"\":return: the dump as bytes.\"\"\"\n        if self.__text_is_expected:\n            return self.string().encode(self.__encoding)\n        else:\n            return self._bytes()\n\n    def _bytes(self):\n        \"\"\":return: bytes from a :class:`io.BytesIO`\"\"\"\n        file = BytesIO()\n        self.__dump_to_file(file)\n        file.seek(0)\n        return file.read()\n\n    def file(self, file=None):\n        \"\"\"Saves the dump in a file-like object in text mode.\n\n        :param file: :obj:`None` or a file-like object.\n        :return: a file-like object\n\n        If :paramref:`file` is :obj:`None`, a new :class:`io.StringIO`\n        is returned.\n        If :paramref:`file` is not :obj:`None` it should be a file-like object.\n\n        The content is written to the file. After writing, the file's\n        read/write position points behind the dumped content.\n        \"\"\"\n        if file is None:\n            file = StringIO()\n        self._file(file)\n        return file\n\n    def _file(self, file):\n        \"\"\"Dump the content to a `file`.\n        \"\"\"\n        if not self.__text_is_expected:\n            file = BytesWrapper(file, self.__encoding)\n        self.__dump_to_file(file)\n\n    def binary_file(self, file=None):\n        \"\"\"Same as :meth:`file` but for binary content.\"\"\"\n        if file is None:\n            file = BytesIO()\n        self._binary_file(file)\n        return file\n\n    def _binary_file(self, file):\n        \"\"\"Dump the ocntent into the `file` in binary mode.\n        \"\"\"\n        if self.__text_is_expected:\n            file = TextWrapper(file, self.__encoding)\n        self.__dump_to_file(file)\n\n    def _mode_and_encoding_for_open(self):\n        \"\"\":return: the file mode and encoding for :obj:`open`.\"\"\"\n        if self.__text_is_expected:\n            return \"w\", self.__encoding\n        return \"wb\", None\n\n    def path(self, path):\n        \"\"\"Saves the dump in a file named :paramref:`path`.\n\n        :param str path: a valid path to a file location. The file can exist.\n        \"\"\"\n        self._path(path)\n\n    def _path(self, path):\n        \"\"\"Saves the dump in a file named `path`.\"\"\"\n        mode, encoding = self._mode_and_encoding_for_open()\n        with open(path, mode, encoding=encoding) as file:\n            self.__dump_to_file(file)\n\n    def _temporary_file(self, delete):\n        \"\"\":return: a temporary file where the content is dumped to.\"\"\"\n        file = NamedTemporaryFile(\"w+\", delete=delete,\n                                  encoding=self.__encoding)\n        self._file(file)\n        return file\n\n    def temporary_path(self, extension=\"\"):\n        \"\"\"Saves the dump in a temporary file and returns its path.\n\n        .. warning:: The user of this method is responsible for deleting this\n                     file to save space on the hard drive.\n                     If you only need a file object for a short period of time\n                     you can use the method :meth:`temporary_file`.\n\n        :param str extension: the ending ot the file name e.g. ``\".png\"``\n        :return: a path to the temporary file\n        :rtype: str\n        \"\"\"\n        path = NamedTemporaryFile(delete=False, suffix=extension).name\n        self.path(path)\n        return path\n\n    def temporary_file(self, delete_when_closed=True):\n        \"\"\"Saves the dump in a temporary file and returns the open file object.\n\n        :param bool delete_when_closed: whether to delete the temporary file\n                                        when it is closed.\n        :return: a file-like object\n\n        If :paramref:`delete_when_closed` is :obj:`True` (default) the file\n        on the hard drive will be deleted if it is closed or not referenced\n        any more.\n\n        If :paramref:`delete_when_closed` is :obj:`False` the returned\n        temporary file is not deleted when closed or unreferenced.\n        The user of this method has then the responsibility to free the\n        space on the host system.\n\n        The returned file-like object has an attribute ``name`` that holds\n        the location of the file.\n        \"\"\"\n        return self._temporary_file(delete_when_closed)\n\n    def binary_temporary_file(self, delete_when_closed=True):\n        \"\"\"Same as :meth:`temporary_file` but for binary mode.\"\"\"\n        return self._binary_temporary_file(delete_when_closed)\n    temporary_binary_file = binary_temporary_file\n\n    def _binary_temporary_file(self, delete):\n        \"\"\":return: a binary temporary file where the content is dumped to.\"\"\"\n        file = NamedTemporaryFile(\"wb+\", delete=delete)\n        self._binary_file(file)\n        return file\n\n    def __repr__(self):\n        \"\"\"the string representation for people to read\n\n        :return: the string representation of this object\n        :rtype: str\n        \"\"\"\n        return \"<{} in with encoding {} >\".format(\n            self.__class__.__name__,\n            self.__encoding\n        )\n\n\n__all__ = [\"ContentDumper\"]\n"
  },
  {
    "path": "knittingpattern/Dumper/json.py",
    "content": "\"\"\"Dump objects to JSON.\"\"\"\nimport json\nfrom .file import ContentDumper\n\n\nclass JSONDumper(ContentDumper):\n\n    \"\"\"This class can be used to dump object s as JSON.\"\"\"\n\n    def __init__(self, on_dump):\n        \"\"\"Create a new JSONDumper object with the callable `on_dump`.\n\n        `on_dump` takes no arguments and returns the object that should be\n        serialized to JSON.\"\"\"\n        super().__init__(self._dump_to_file)\n        self.__dump_object = on_dump\n\n    def object(self):\n        \"\"\"Return the object that should be dumped.\"\"\"\n        return self.__dump_object()\n\n    def _dump_to_file(self, file):\n        \"\"\"dump to the file\"\"\"\n        json.dump(self.object(), file)\n\n    def knitting_pattern(self, specification=None):\n        \"\"\"loads a :class:`knitting pattern\n        <knittingpattern.KnittingPattern.KnittingPattern>` from the dumped\n        content\n\n        :param specification: a\n          :class:`~knittingpattern.ParsingSpecification.ParsingSpecification`\n          or :obj:`None` to use the default specification\"\"\"\n        from ..ParsingSpecification import new_knitting_pattern_set_loader\n        if specification is None:\n            loader = new_knitting_pattern_set_loader()\n        else:\n            loader = new_knitting_pattern_set_loader(specification)\n        return loader.object(self.object())\n\n__all__ = [\"JSONDumper\"]\n"
  },
  {
    "path": "knittingpattern/Dumper/svg.py",
    "content": "\"\"\"Dump objects to SVG.\"\"\"\nfrom .xml import XMLDumper\nfrom os import remove as remove_file\n\n\nclass SVGDumper(XMLDumper):\n\n    \"\"\"This class dumps objects to SVG.\"\"\"\n\n    def kivy_svg(self):\n        \"\"\"An SVG object.\n\n        :return: an SVG object\n        :rtype: kivy.graphics.svg.Svg\n        :raises ImportError: if the module was not found\n        \"\"\"\n        from kivy.graphics.svg import Svg\n        path = self.temporary_path(\".svg\")\n        try:\n            return Svg(path)\n        finally:\n            remove_file(path)\n\n__all__ = [\"SVGDumper\"]\n"
  },
  {
    "path": "knittingpattern/Dumper/xml.py",
    "content": "\"\"\"Dump objects to XML.\"\"\"\nimport xmltodict\nfrom .file import ContentDumper\n\n\nclass XMLDumper(ContentDumper):\n\n    \"\"\"Used to dump objects as XML.\"\"\"\n\n    def __init__(self, on_dump):\n        \"\"\"Create a new XMLDumper object with the callable `on_dump`.\n\n        `on_dump` takes no aguments and returns the object that should be\n        serialized to XML.\"\"\"\n        super().__init__(self._dump_to_file)\n        self.__dump_object = on_dump\n\n    def object(self):\n        \"\"\"Return the object that should be dumped.\"\"\"\n        return self.__dump_object()\n\n    def _dump_to_file(self, file):\n        \"\"\"dump to the file\"\"\"\n        xmltodict.unparse(self.object(), file, pretty=True)\n\n__all__ = [\"XMLDumper\"]\n"
  },
  {
    "path": "knittingpattern/IdCollection.py",
    "content": "\"\"\"See this module if you like to store object s that have an ``id`` attribute.\n\"\"\"\nfrom collections import OrderedDict\n\n\nclass IdCollection(object):\n    \"\"\"This is a collections of object that have an ``id`` attribute.\"\"\"\n\n    def __init__(self):\n        \"\"\"Create a new :class:`IdCollection` with no arguments.\n\n        You can add objects later using the method :meth:`append`.\n        \"\"\"\n        self._items = OrderedDict()\n\n    def append(self, item):\n        \"\"\"Add an object to the end of the :class:`IdCollection`.\n\n        :param item: an object that has an id\n        \"\"\"\n        self._items[item.id] = item\n\n    def at(self, index):\n        \"\"\"Get the object at an :paramref:`index`.\n\n        :param int index: the index of the object\n        :return: the object at :paramref:`index`\n        \"\"\"\n        keys = list(self._items.keys())\n        key = keys[index]\n        return self[key]\n\n    def __getitem__(self, id_):\n        \"\"\"Get the object with the :paramref:`id`\n\n        .. code:: python\n\n            ic = IdCollection()\n            ic.append(object_1)\n            ic.append(object_2)\n            assert ic[object_1.id] == object_1\n            assert ic[object_2.id] == object_1\n\n        :param id_: the id of an object\n        :return: the object with the :paramref:`id`\n        :raises KeyError: if no object with :paramref:`id` was found\n        \"\"\"\n        return self._items[id_]\n\n    def __bool__(self):\n        \"\"\":return: whether there is anything in the collection.\n        :rtype: bool\n        \"\"\"\n        return bool(self._items)\n\n    def __iter__(self):\n        \"\"\"allows you to iterate and use for-loops\n\n        The objects in the iterator have the order in which they were appended.\n        \"\"\"\n        for id_ in self._items:\n            yield self[id_]\n\n    def __len__(self):\n        \"\"\":return: the number of objects in this collection\"\"\"\n        return len(self._items)\n\n    @property\n    def first(self):\n        \"\"\"The first element in this collection.\n\n        :return: the first element in this collection\n        :raises IndexError: if this collection is empty\n        \"\"\"\n        return self.at(0)\n"
  },
  {
    "path": "knittingpattern/Instruction.py",
    "content": "\"\"\"Knitting patterns consist of instructions.\n\nThe :class:`instructions <Instruction>`. that are used in the\n:class:`knitting patterns <KnittingPattern>` can be foudn in this module.\nThey have certain attributes in common.\n\n\"\"\"\nfrom .Prototype import Prototype\nfrom .Mesh import ProducedMesh, ConsumedMesh\nfrom .convert.color import convert_color_to_rrggbb\n\n\n# pattern specification\n\nID = \"id\"  #: the id key in the specification\n\nTYPE = \"type\"  #: the type key in the specification\n\nKNIT_TYPE = \"knit\"  #: the type of the knit instruction\n\nPURL_TYPE = \"purl\"  #: the type of the purl instruction\n\n#: the type of the instruction without a specified type\nDEFAULT_TYPE = KNIT_TYPE\n\nCOLOR = \"color\"  #: the color key in the specification\n\nDESCRIPTION = \"description\"  #: the description in the specification\n\n#: the key for the number of meshes that a instruction consumes\nNUMBER_OF_CONSUMED_MESHES = \"number of consumed meshes\"\n\n#: the default number of meshes that a instruction consumes\nDEFAULT_NUMBER_OF_CONSUMED_MESHES = 1\n\n#: the key for the number of meshes that a instruction produces\nNUMBER_OF_PRODUCED_MESHES = \"number of produced meshes\"\n\n#: the default number of meshes that a instruction produces\nDEFAULT_NUMBER_OF_PRODUCED_MESHES = 1\n\n#: The default z-index, see :func:`get_z`.\nDEFAULT_Z = 0\n\n#: Instructions have a default specification. In this specification the key\n#: in :data:`RENDER` points to configuration for rendering.\nRENDER = \"render\"\n\n#: The key to look for the z-index inside the :data:`render` specification.\n#: .. seealso:: :func:`get_z`, :data:`DEFAULT_Z`\nRENDER_Z = \"z\"\n\n# error messages\n\nINSTRUCTION_NOT_FOUND_MESSAGE = \\\n    \"Instruction {instruction} was not found in row {row}.\"\n\n\nclass Instruction(Prototype):\n\n    \"\"\"Instructions specify what should be done during knitting.\n\n    This class represents the basic interface for instructions.\n\n    It is based on the\n    :class:`Prototype <knittingpattern.Prototype.Prototype>`\n    which allows creating instructions based on other instructions so\n    they can inherit their attributes.\n\n    You can create new instructions by passing a specification to them which\n    can consist of a :class:`dictionary <dict>` or an other\n    :class:`prototype <knittingpattern.Prototype.Prototype>`.\n    For such specifications see the\n    :mod:`InstructionLibrary <knittingpattern.InstructionLibrary>`.\n    \"\"\"\n\n    @property\n    def id(self):\n        \"\"\"The id of the instruction.\n\n        :return: the :data:`id <ID>` of the instruction or\n          :obj:`None` if none is specified.\n        \"\"\"\n        return self.get(ID)\n\n    @property\n    def type(self):\n        \"\"\"The type of the instruction.\n\n        :return: the :data:`type <TYPE>` of the instruction or\n          :data:`DEFAULT_TYPE` if none is specified.\n        :rtype: str\n\n        The type should be a string.\n        Depending on the type, the instruction can receive additional\n        attributes.\n\n        .. seealso:: :mod:`knittingpattern.InstructionLibrary`\n        \"\"\"\n        return self.get(TYPE, DEFAULT_TYPE)\n\n    @property\n    def color(self):\n        \"\"\"The color of the instruction.\n\n        :return: the :data:`color <COLOR>` of the instruction or\n          :obj:`None` if none is specified.\n        \"\"\"\n        return self.get(COLOR)\n\n    @property\n    def colors(self):\n        \"\"\"All the colors that an instruction has.\n\n        :return: a list of colors of the instruction. If the instruction has\n          no color, this is ``[None]``.\n        :rtype: list\n        \"\"\"\n        return [self.color]\n\n    @property\n    def description(self):\n        \"\"\"The description of the instruction.\n\n        :return: the :data:`description <DESCRIPTION>` of the instruction or\n          :obj:`None` if none is specified.\n        \"\"\"\n        return self.get(DESCRIPTION)\n\n    @property\n    def number_of_consumed_meshes(self):\n        \"\"\"The number of meshes that this instruction consumes.\n\n        :return: the :data:`number of consumed meshes\n          <NUMBER_OF_CONSUMED_MESHES>` of the instruction or\n          :data:`DEFAULT_NUMBER_OF_CONSUMED_MESHES` if none is specified.\n        \"\"\"\n        return self.get(NUMBER_OF_CONSUMED_MESHES,\n                        DEFAULT_NUMBER_OF_CONSUMED_MESHES)\n\n    @property\n    def number_of_produced_meshes(self):\n        \"\"\"The number of meshes that this instruction produces.\n\n        :return: the :data:`number of produced meshes\n          <NUMBER_OF_PRODUCED_MESHES>` of the instruction or\n          :data:`DEFAULT_NUMBER_OF_PRODUCED_MESHES` if none is specified.\n        \"\"\"\n        return self.get(NUMBER_OF_PRODUCED_MESHES,\n                        DEFAULT_NUMBER_OF_PRODUCED_MESHES)\n\n    def has_color(self):\n        \"\"\"Whether this instruction has a color.\n\n        :return: whether a :data:`color <COLOR>` is specified\n        :rtype: bool\n        \"\"\"\n        return self.color is not None\n\n    def does_knit(self):\n        \"\"\"Whether this instruction is a knit instruction.\n\n        :return: whether this instruction is a knit instruction\n        :rtype: bool\n        \"\"\"\n        return self.type == KNIT_TYPE\n\n    def does_purl(self):\n        \"\"\"Whether this instruction is a purl instruction.\n\n        :return: whether this instruction is a purl instruction\n        :rtype: bool\n        \"\"\"\n        return self.type == PURL_TYPE\n\n    def produces_meshes(self):\n        \"\"\"Whether this institution produces meshes.\n\n        :return: whether this instruction produces any meshes\n        :rtype: bool\n\n        .. seealso:: :attr:`number_of_produced_meshes`\n        \"\"\"\n        return self.number_of_produced_meshes != 0\n\n    def consumes_meshes(self):\n        \"\"\"Whether this instruction consumes meshes.\n\n        :return: whether this instruction consumes any meshes\n        :rtype: bool\n\n        .. seealso:: :attr:`number_of_consumed_meshes`\n        \"\"\"\n        return self.number_of_consumed_meshes != 0\n\n    @property\n    def render_z(self):\n        \"\"\"The z-index of the instruction when rendered.\n\n        :return: the z-index of the instruction. Instructions with a higher\n          z-index are displayed in front of instructions with lower z-index.\n        :rtype: float\n        \"\"\"\n        return self.get(RENDER, {}).get(RENDER_Z, DEFAULT_Z)\n\n    @property\n    def hex_color(self):\n        \"\"\"The color in \"#RRGGBB\" format.\n\n        :return: the :attr:`color` in \"#RRGGBB\" format or none if no color is\n          given\n        \"\"\"\n        if self.has_color():\n            return convert_color_to_rrggbb(self.color)\n        return None\n\n    def to_svg(self, converter=None):\n        \"\"\"Return a SVGDumper for this instruction.\n\n        :param converter: a :class:`\n          knittingpattern.convert.InstructionSVGCache.InstructionSVGCache` or\n          :obj:`None`. If :obj:`None` is given, the :func:`\n          knittingpattern.convert.InstructionSVGCache.default_svg_cache` is\n          used.\n        :rtype: knittingpattern.Dumper.SVGDumper\n        \"\"\"\n        if converter is None:\n            from knittingpattern.convert.InstructionSVGCache import \\\n                default_svg_cache\n            converter = default_svg_cache()\n        return converter.to_svg(self)\n\n\nclass InstructionInRow(Instruction):\n\n    \"\"\"Instructions can be placed in rows.\n\n    Then, they have additional attributes and properties.\n    \"\"\"\n\n    def __init__(self, row, spec):\n        \"\"\"Create a new instruction in a row with a specification.\n\n        :param knittingpattern.Row.Row row: the row the instruction is placed\n          in\n        :param spec: specification of the instruction\n        \"\"\"\n        super().__init__(spec)\n        self._row = row\n        self._produced_meshes = [\n            self._new_produced_mesh(self, index)\n            for index in range(self.number_of_produced_meshes)\n        ]\n        self._consumed_meshes = [\n            self._new_consumed_mesh(self, index)\n            for index in range(self.number_of_consumed_meshes)\n        ]\n        self._cached_index_in_row = None\n\n    def transfer_to_row(self, new_row):\n        \"\"\"Transfer this instruction to a new row.\n\n        :param knittingpattern.Row.Row new_row: the new row the instruction is\n          in.\n        \"\"\"\n        if new_row != self._row:\n            index = self.get_index_in_row()\n            if index is not None:\n                self._row.instructions.pop(index)\n            self._row = new_row\n\n    @property\n    def _new_produced_mesh(self):\n        \"\"\":return: the class of the produced meshes.\"\"\"\n        return ProducedMesh\n\n    @property\n    def _new_consumed_mesh(self):\n        \"\"\":return: the class of the consumed meshes.\"\"\"\n        return ConsumedMesh\n\n    @property\n    def row(self):\n        \"\"\"The row this instruction is in.\n\n        :return: the row the instruction is placed in\n        :rtype: knittingpattern.Row.Row\n        \"\"\"\n        return self._row\n\n    def is_in_row(self):\n        \"\"\"Whether the instruction can be found in its row.\n\n        :return: whether the instruction is in its row\n        :rtype: bool\n\n        Use this to avoid raising and :class:`InstructionNotFoundInRow`.\n        \"\"\"\n        return self.get_index_in_row() is not None\n\n    def get_index_in_row(self):\n        \"\"\"Index of the instruction in the instructions of the row or None.\n\n        :return: index in the :attr:`row`'s instructions or None, if the\n          instruction is not in the row\n        :rtype: int\n\n        .. seealso:: :attr:`row_instructions`, :attr:`index_in_row`,\n          :meth:`is_in_row`\n        \"\"\"\n        expected_index = self._cached_index_in_row\n        instructions = self._row.instructions\n        if expected_index is not None and \\\n                0 <= expected_index < len(instructions) and \\\n                instructions[expected_index] is self:\n            return expected_index\n        for index, instruction_in_row in enumerate(instructions):\n            if instruction_in_row is self:\n                self._cached_index_in_row = index\n                return index\n        return None\n\n    @property\n    def index_in_row(self):\n        \"\"\"Index of the instruction in the instructions of the row.\n\n        :return: index in the :attr:`row`'s instructions\n        :rtype: int\n        :raises knittingpattern.Instruction.InstructionNotFoundInRow:\n          if the instruction is not found at the index\n\n        .. code:: python\n\n            index = instruction.index_in_row\n            assert instruction.row.instructions[index] == instruction\n\n        .. seealso:: :attr:`row_instructions`, :meth:`get_index_in_row`,\n          :meth:`is_in_row`\n        \"\"\"\n        index = self.get_index_in_row()\n        if index is None:\n            self._raise_not_found_error()\n        return index\n\n    @property\n    def row_instructions(self):\n        \"\"\"Shortcut for ``instruction.row.instructions``.\n\n        :return: the instructions of the :attr:`row` the instruction is in\n\n        .. seealso:: :attr:`index_in_row`\n        \"\"\"\n        return self.row.instructions\n\n    @property\n    def next_instruction_in_row(self):\n        \"\"\"The instruction after this one or None.\n\n        :return: the instruction in :attr:`row_instructions` after this or\n          :obj:`None` if this is the last\n        :rtype: knittingpattern.Instruction.InstructionInRow\n\n        This can be used to traverse the instructions.\n\n        .. seealso:: :attr:`previous_instruction_in_row`\n        \"\"\"\n        index = self.index_in_row + 1\n        if index >= len(self.row_instructions):\n            return None\n        return self.row_instructions[index]\n\n    @property\n    def previous_instruction_in_row(self):\n        \"\"\"The instruction before this one or None.\n\n        :return: the instruction in :attr:`row_instructions` before this or\n          :obj:`None` if this is the first\n        :rtype: knittingpattern.Instruction.InstructionInRow\n\n        This can be used to traverse the instructions.\n\n        .. seealso:: :attr:`next_instruction_in_row`\n        \"\"\"\n        index = self.index_in_row - 1\n        if index < 0:\n            return None\n        return self.row_instructions[index]\n\n    @property\n    def _instruction_not_found_message(self):\n        \"\"\"The message for the error.\n\n        :return: an error message\n        :rtype: str\n\n        .. warning: private, do not use\n        \"\"\"\n        return INSTRUCTION_NOT_FOUND_MESSAGE.format(\n            instruction=self, row=self.row\n        )\n\n    def _raise_not_found_error(self):\n        \"\"\"Raise an error that this instruction is in its row no longer.\n\n        :raises knittingpattern.Instruction.InstructionNotFoundInRow:\n          the instruction was not found\n\n        .. warning: private, do not use\n        \"\"\"\n        raise InstructionNotFoundInRow(self._instruction_not_found_message)\n\n    @property\n    def index_of_first_produced_mesh_in_row(self):\n        \"\"\"Index of the first produced mesh in the row that consumes it.\n\n        :return: an index of the first produced mesh of rows produced meshes\n        :rtype: int\n\n        .. note:: If the instruction :meth:`produces meshes\n          <Instruction.produces_meshes>`, this is the index of the first\n          mesh the instruction produces in all the meshes of the row.\n          If the instruction does not produce meshes, the index of the mesh is\n          returned as if the instruction had produced a mesh.\n\n        .. code::\n\n            if instruction.produces_meshes():\n                index = instruction.index_of_first_produced_mesh_in_row\n\n        \"\"\"\n        index = 0\n        for instruction in self.row_instructions:\n            if instruction is self:\n                break\n            index += instruction.number_of_produced_meshes\n        else:\n            self._raise_not_found_error()\n        return index\n\n    @property\n    def index_of_last_produced_mesh_in_row(self):\n        \"\"\"Index of the last mesh produced by this instruction in its row.\n\n        :return: an index of the last produced mesh of rows produced meshes\n        :rtype: int\n\n        .. note:: If this instruction :meth:`produces meshes\n          <Instruction.produces_meshes>`, this is the index of\n          its last produces mesh in the row. However, if this instruction does\n          not produce meshes, this is the index **before** the first mesh of\n          the instruction if it produced meshes.\n\n        .. seealso:: :attr:`index_of_first_produced_mesh_in_row`\n        \"\"\"\n        index = self.index_of_first_produced_mesh_in_row\n        return index + self.number_of_produced_meshes - 1\n\n    @property\n    def index_of_first_consumed_mesh_in_row(self):\n        \"\"\"The index of the first consumed mesh of this instruction in its row.\n\n        Same as :attr:`index_of_first_produced_mesh_in_row`\n        but for consumed meshes.\n        \"\"\"\n        index = 0\n        for instruction in self.row_instructions:\n            if instruction is self:\n                break\n            index += instruction.number_of_consumed_meshes\n        else:\n            self._raise_not_found_error()\n        return index\n\n    @property\n    def index_of_last_consumed_mesh_in_row(self):\n        \"\"\"The index of the last consumed mesh of this instruction in its row.\n\n        Same as :attr:`index_of_last_produced_mesh_in_row`\n        but for the last consumed mesh.\n        \"\"\"\n        index = self.index_of_first_consumed_mesh_in_row\n        return index + self.number_of_consumed_meshes - 1\n\n    @property\n    def produced_meshes(self):\n        \"\"\"The meshes produced by this instruction\n\n        :return: a :class:`list` of :class:`meshes\n          <knittingpattern.Mesh.Mesh>` that this instruction produces\n        :rtype: list\n\n        .. code:: python\n\n            assert len(inst.produced_meshes) == inst.number_of_produced_meshes\n            assert all(mesh.is_produced() for mesh in inst.produced_meshes)\n\n        .. seealso:: :attr:`consumed_meshes`, :attr:`consuming_instructions`\n        \"\"\"\n        return self._produced_meshes\n\n    @property\n    def consumed_meshes(self):\n        \"\"\"The meshes consumed by this instruction\n\n        :return: a :class:`list` of :class:`meshes\n          <knittingpattern.Mesh.Mesh>` that this instruction consumes\n        :rtype: list\n\n        .. code:: python\n\n            assert len(inst.consumed_meshes) == inst.number_of_consumed_meshes\n            assert all(mesh.is_consumed() for mesh in inst.consumed_meshes)\n\n        .. seealso:: :attr:`produced_meshes`, :attr:`producing_instructions`\n        \"\"\"\n        return self._consumed_meshes\n\n    def __repr__(self):\n        \"\"\":obj:`repr(instruction) <repr>` used for :func:`print`.\n\n        :return: the string representation of this object\n        :rtype: str\n        \"\"\"\n        index = self.get_index_in_row()\n        if index is None:\n            position = \"not in {}\".format(self.row)\n        else:\n            position = \"in {} at {}\".format(self.row, index)\n        return \"<{} {}\\\"{}\\\" {}>\".format(\n            self.__class__.__name__,\n            (\"{} \".format(self.id) if self.id is not None else \"\"),\n            self.type,\n            position\n        )\n\n    @property\n    def producing_instructions(self):\n        \"\"\"Instructions that produce the meshes that this instruction consumes.\n\n        :return: a list of :class:`instructions\n          <knittingpattern.Instruction.InstructionInRow>`\n        :rtype: list\n\n        .. seealso:: :attr:`consuming_instructions`, :attr:`consumed_meshes`\n        \"\"\"\n        return [(mesh.producing_instruction if mesh.is_produced() else None)\n                for mesh in self.consumed_meshes]\n\n    @property\n    def consuming_instructions(self):\n        \"\"\"Instructions that consume the meshes that this instruction produces.\n\n        :return: a list of :class:`instructions\n          <knittingpattern.Instruction.InstructionInRow>`\n        :rtype: list\n\n        .. seealso:: :attr:`producing_instructions`, :attr:`produced_meshes`\n        \"\"\"\n        return [(mesh.consuming_instruction if mesh.is_consumed() else None)\n                for mesh in self.produced_meshes]\n\n    @property\n    def color(self):\n        \"\"\"The color of the instruction.\n\n        :return: the :data:`color <COLOR>` of the instruction or\n          :obj:`None` if none is specified.\n\n        If no color is specified in the instruction, it is inherited form the\n        row.\n        \"\"\"\n        return self.get(COLOR, self.row.color)\n\n    @property\n    def last_produced_mesh(self):\n        \"\"\"The last produced mesh.\n\n        :return: the last produced mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is produced\n\n        .. seealso:: :attr:`Instruction.number_of_produced_meshes`\n        \"\"\"\n        return self._produced_meshes[-1]\n\n    @property\n    def last_consumed_mesh(self):\n        \"\"\"The last consumed mesh.\n\n        :return: the last consumed mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is consumed\n\n        .. seealso:: :attr:`Instruction.number_of_consumed_meshes`\n        \"\"\"\n        return self._consumed_meshes[-1]\n\n    @property\n    def first_produced_mesh(self):\n        \"\"\"The first produced mesh.\n\n        :return: the first produced mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is produced\n\n        .. seealso:: :attr:`Instruction.number_of_produced_meshes`\n        \"\"\"\n        return self._produced_meshes[0]\n\n    @property\n    def first_consumed_mesh(self):\n        \"\"\"The first consumed mesh.\n\n        :return: the first consumed mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is consumed\n\n        .. seealso:: :attr:`Instruction.number_of_consumed_meshes`\n        \"\"\"\n        return self._consumed_meshes[0]\n\n\nclass InstructionNotFoundInRow(ValueError):\n    \"\"\"This exception is raised if an instruction was not found in its row.\"\"\"\n    pass\n\n\n__all__ = [\"Instruction\", \"InstructionInRow\", \"InstructionNotFoundInRow\",\n           \"ID\", \"TYPE\", \"KNIT_TYPE\", \"PURL_TYPE\", \"DEFAULT_TYPE\", \"COLOR\",\n           \"NUMBER_OF_CONSUMED_MESHES\", \"DEFAULT_NUMBER_OF_CONSUMED_MESHES\",\n           \"NUMBER_OF_PRODUCED_MESHES\", \"DEFAULT_NUMBER_OF_PRODUCED_MESHES\",\n           \"RENDER_Z\", \"RENDER\", \"DEFAULT_Z\"]\n"
  },
  {
    "path": "knittingpattern/InstructionLibrary.py",
    "content": "\"\"\"Instructions have  many attributes that do not need to be specified\nin each :class:`knitting pattern set\n<knittingpattern.KnittingpatternSet.KnittingpatternSet>`.\n\nThis module provides the functionality to load default values for instructions\nfrom various locations.\n\"\"\"\nfrom .Instruction import TYPE\nfrom .Loader import JSONLoader\nfrom .Instruction import Instruction\n\n\nclass InstructionLibrary(object):\n    \"\"\"This library can be used to look up default specification of\n    instructions.\n\n    The specification is searched for by the type of the instruction.\n    \"\"\"\n\n    @property\n    def _loader_class(self):\n        \"\"\":return: the class for loading the specifications with\n          :attr:`load`\n        \"\"\"\n        return JSONLoader\n\n    @property\n    def _instruction_class(self):\n        \"\"\":return: the class for the specifications\n        \"\"\"\n        return Instruction\n\n    def __init__(self):\n        \"\"\"Create a new :class:`InstructionLibrary\n        <knittingpattern.InstructionLibrary.InstructionLibrary>` without\n        arguments.\n\n        Use :attr:`load` to load specifications.\n        \"\"\"\n        self._type_to_instruction = {}\n\n    @property\n    def load(self):\n        \"\"\":return: a loader that can be used to load specifications\n        :rtype: knittingpattern.Loader.JSONLoader\n\n        A file to load is a list of instructions in JSON format.\n\n        .. code:: json\n\n            [\n                {\n                    \"type\" : \"knit\",\n                    \"another\" : \"attribute\"\n                },\n                {\n                    \"type\" : \"purl\"\n                }\n            ]\n\n        \"\"\"\n        return self._loader_class(self._process_loaded_object)\n\n    def _process_loaded_object(self, obj):\n        \"\"\"add the loaded instructions from :attr:`load`\n        \"\"\"\n        for instruction in obj:\n            self.add_instruction(instruction)\n        return self\n\n    def add_instruction(self, specification):\n        \"\"\"Add an instruction specification\n\n        :param specification: a specification with a key\n          :data:`knittingpattern.Instruction.TYPE`\n\n        .. seealso:: :meth:`as_instruction`\n        \"\"\"\n        instruction = self.as_instruction(specification)\n        self._type_to_instruction[instruction.type] = instruction\n\n    def as_instruction(self, specification):\n        \"\"\"Convert the specification into an instruction\n\n        :param specification: a specification with a key\n          :data:`knittingpattern.Instruction.TYPE`\n\n        The instruction is not added.\n\n        .. seealso:: :meth:`add_instruction`\n        \"\"\"\n        instruction = self._instruction_class(specification)\n        type_ = instruction.type\n        if type_ in self._type_to_instruction:\n            instruction.inherit_from(self._type_to_instruction[type_])\n        return instruction\n\n    def __getitem__(self, instruction_type):\n        \"\"\":return: the specification for :paramref:`instruction_type`\n\n        .. seealso:: :meth:`as_instruction`\n        \"\"\"\n        return self.as_instruction({TYPE: instruction_type})\n\n    @property\n    def loaded_types(self):\n        \"\"\"The types loaded in this library.\n\n        :return: a list of types, preferably as :class:`string <str>`\n        :rtype: list\n        \"\"\"\n        return list(self._type_to_instruction)\n\n\nclass DefaultInstructions(InstructionLibrary):\n    \"\"\"The default specifications for instructions ported with this package\n    \"\"\"\n\n    #: the folder relative to this module where the instructions are located\n    INSTRUCTIONS_FOLDER = \"instructions\"\n\n    def __init__(self):\n        \"\"\"Create the default instruction library without arguments.\n\n        The default specifications are loaded automatically form this package.\n        \"\"\"\n        super().__init__()\n        self.load.relative_folder(__file__, self.INSTRUCTIONS_FOLDER)\n\n\ndef default_instructions():\n    \"\"\":return: a default instruction library\n    :rtype: DefaultInstructions\n\n    .. warning:: The return value is mutable and you should not add new\n      instructions to it. If you would like to add instructions to it,\n      create a new\n      :class:`~knittingpattern.InstructionLibrary.DefaultInstructions`\n      instance.\n    \"\"\"\n    global _default_instructions\n    if _default_instructions is None:\n        _default_instructions = DefaultInstructions()\n    return _default_instructions\n\n\n_default_instructions = None\n__all__ = [\"InstructionLibrary\", \"DefaultInstructions\", \"default_instructions\"]\n"
  },
  {
    "path": "knittingpattern/KnittingPattern.py",
    "content": "\"\"\"Here you can find the set of knit instructions in rows.\n\nA :class:`knitting pattern set\n<knittingpattern.KnittingPatternSet.KnittingPatternSet>`\nconsists of several :class:`KnittingPatterns\n<knittingpattern.KnittingPattern.KnittingPattern>`.\nTheir functionality can be found in this module.\n\"\"\"\nfrom .walk import walk\nfrom .utils import unique\n\n\nclass KnittingPattern(object):\n    \"\"\"Knitting patterns contain a set of instructions that form a pattern.\n\n    Usually you do not create instances of this but rather load a\n    :class:`knitting pattern set\n    <knittingpattern.KnittingPatternSet.KnittingPatternSet>`.\n    \"\"\"\n\n    def __init__(self, id_, name, rows, parser):\n        \"\"\"Create a new instance.\n\n        :param id_: the id of this pattern\n        :param name: the human readable name of this pattern\n        :param rows: a collection of rows of instructions\n        :param knittingpattern.Parser.Parser parser: the parser to use to new\n          content\n\n        .. seealso:: :func:`knittingpattern.new_knitting_pattern`\n        \"\"\"\n        self._id = id_\n        self._name = name\n        self._rows = rows\n        self._parser = parser\n\n    @property\n    def id(self):\n        \"\"\"the identifier within a :class:`set of knitting patterns\n        <knittingpattern.KnittingPatternSet.KnittingPatternSet>`\n        \"\"\"\n        return self._id\n\n    @property\n    def name(self):\n        \"\"\"a human readable name\"\"\"\n        return self._name\n\n    @property\n    def rows(self):\n        \"\"\"a collection of rows that this pattern is made of\n\n        Usually this should be a\n        :class:`knittingpattern.IdCollection.IdCollection` of\n        :class:`knittingpattern.Row.Row`.\"\"\"\n        return self._rows\n\n    def add_row(self, id_):\n        \"\"\"Add a new row to the pattern.\n\n        :param id_: the id of the row\n        \"\"\"\n        row = self._parser.new_row(id_)\n        self._rows.append(row)\n        return row\n\n    def rows_in_knit_order(self):\n        \"\"\"Return the rows in the order that they should be knit.\n\n        :rtype: list\n        :return: the :attr:`rows` in the order that they should be knit\n\n        .. seealso:: :mod:`knittingpattern.walk`\n        \"\"\"\n        return walk(self)\n\n    @property\n    def instruction_colors(self):\n        \"\"\"The colors of the instructions.\n\n        :return: the colors of the instructions listed in first appearance in\n          knit order\n        :rtype: list\n        \"\"\"\n        return unique([row.instruction_colors\n                       for row in self.rows_in_knit_order()])\n\n__all__ = [\"KnittingPattern\"]\n"
  },
  {
    "path": "knittingpattern/KnittingPatternSet.py",
    "content": "\"\"\"A set of knitting patterns that can be dumped and loaded.\"\"\"\n\nfrom .convert.AYABPNGDumper import AYABPNGDumper\nfrom .Dumper import XMLDumper\nfrom .convert.InstructionSVGCache import default_instruction_svg_cache\nfrom .convert.Layout import GridLayout\nfrom .convert.SVGBuilder import SVGBuilder\nfrom .convert.KnittingPatternToSVG import KnittingPatternToSVG\n\n\nclass KnittingPatternSet(object):\n\n    \"\"\"This is the class for a set of knitting patterns.\n\n    The :class:`knitting patterns\n    <knittingpattern.KnittingPattern.KnittingPattern>` all have an id and can\n    be accessed from here. It is possible to load this set of knitting patterns\n    from various locations, see the :mod:`knittingpattern` module.\n    You rarely need to create such a pattern yourself. It is easier to create\n    the pattern by loading it from a file.\n    \"\"\"\n\n    def __init__(self, type_, version, patterns, parser, comment=None):\n        \"\"\"Create a new knitting pattern set.\n\n        This is the class for a set of :class:`knitting patterns\n        <knittingpattern.KnittingPattern.KnittingPattern>`.\n\n        :param str type: the type of the knitting pattern set, see the\n          :ref:`specification <FileFormatSpecification>`.\n        :param str version: the version of the knitting pattern set.\n          This is not the version of the library but the version of the\n          :ref:`specification <FileFormatSpecification>`.\n        :param patterns: a collection of patterns. This should be a\n          :class:`~knittingpattern.IdCollection.IdCollection` of\n          :class:`KnittingPatterns\n          <knittingpattern.KnittingPattern.KnittingPattern>`.\n        :param comment: a comment about the knitting pattern\n        \"\"\"\n        self._version = version\n        self._type = type_\n        self._patterns = patterns\n        self._comment = comment\n        self._parser = parser\n\n    @property\n    def version(self):\n        \"\"\"The version of the knitting pattern specification.\n\n        :return: the version of the knitting pattern, see :meth:`__init__`\n        :rtype: str\n\n        .. seealso:: :ref:`FileFormatSpecification`\n        \"\"\"\n        return self._version\n\n    @property\n    def type(self):\n        \"\"\"The type of the knitting pattern.\n\n        :return: the type of the knitting pattern, see :meth:`__init__`\n        :rtype: str\n\n        .. seealso:: :ref:`FileFormatSpecification`\n        \"\"\"\n        return self._type\n\n    @property\n    def patterns(self):\n        \"\"\"The pattern contained in this set.\n\n        :return: the patterns of the knitting pattern, see :meth:`__init__`\n        :rtype: knittingpattern.IdCollection.IdCollection\n\n        The patterns can be accessed by their id.\n        \"\"\"\n        return self._patterns\n\n    @property\n    def comment(self):\n        \"\"\"The comment about the knitting pattern.\n\n        :return: the comment for the knitting pattern set or None,\n          see :meth:`__init__`.\n        \"\"\"\n        return self._comment\n\n    def to_ayabpng(self):\n        \"\"\"Convert the knitting pattern to a png.\n\n        :return: a dumper to save this pattern set as png for the AYAB\n          software\n        :rtype: knittingpattern.convert.AYABPNGDumper.AYABPNGDumper\n\n        Example:\n\n        .. code:: python\n\n            >>> knitting_pattern_set.to_ayabpng().temporary_path()\n            \"/the/path/to/the/file.png\"\n\n        \"\"\"\n        return AYABPNGDumper(lambda: self)\n\n    def to_svg(self, zoom):\n        \"\"\"Create an SVG from the knitting pattern set.\n\n        :param float zoom: the height and width of a knit instruction\n        :return: a dumper to save the svg to\n        :rtype: knittingpattern.Dumper.XMLDumper\n\n        Example:\n\n        .. code:: python\n\n            >>> knitting_pattern_set.to_svg(25).temporary_path(\".svg\")\n            \"/the/path/to/the/file.svg\"\n        \"\"\"\n        def on_dump():\n            \"\"\"Dump the knitting pattern to the file.\n\n            :return: the SVG XML structure as dictionary.\n            \"\"\"\n            knitting_pattern = self.patterns.at(0)\n            layout = GridLayout(knitting_pattern)\n            instruction_to_svg = default_instruction_svg_cache()\n            builder = SVGBuilder()\n            kp_to_svg = KnittingPatternToSVG(knitting_pattern, layout,\n                                             instruction_to_svg, builder, zoom)\n            return kp_to_svg.build_SVG_dict()\n        return XMLDumper(on_dump)\n\n    def add_new_pattern(self, id_, name=None):\n        \"\"\"Add a new, empty knitting pattern to the set.\n\n        :param id_: the id of the pattern\n        :param name: the name of the pattern to add or if :obj:`None`, the\n          :paramref:`id_` is used\n        :return: a new, empty knitting pattern\n        :rtype: knittingpattern.KnittingPattern.KnittingPattern\n        \"\"\"\n        if name is None:\n            name = id_\n        pattern = self._parser.new_pattern(id_, name)\n        self._patterns.append(pattern)\n        return pattern\n\n    @property\n    def first(self):\n        \"\"\"The first element in this set.\n\n        :rtype: knittingpattern.KnittingPattern.KnittingPattern\n        \"\"\"\n        return self._patterns.first\n\n\n__all__ = [\"KnittingPatternSet\"]\n"
  },
  {
    "path": "knittingpattern/Loader.py",
    "content": "\"\"\"One can load objects from different locations.\nThis module provides functionality to load objects from different locations\nwhile preserving a simple interface to the consumer.\n\n\"\"\"\nimport json\nimport os\nimport sys\n\n\ndef identity(object_):\n    \"\"\":return: the argument\n    :param object_: the object to be returned\"\"\"\n    return object_\n\n\ndef true(_):\n    \"\"\":return: :obj:`True`\n    :param _: can be ignored\"\"\"\n    return True\n\n\nclass PathLoader(object):\n    \"\"\"Load paths and folders from the local file system.\n\n    The :paramref:`process <PathLoader.__init__.process>` is called with a\n    :class:`path <str>` as first argument: ``process(path)``.\n    \"\"\"\n\n    def __init__(self, process=identity, chooses_path=true):\n        \"\"\"Create a PathLoader object.\n\n        :param process: ``process(path)`` is called with the `path` to load.\n          The result of :paramref:`process` is returned to the caller. The\n          default value is :func:`identity`, so the paths are returned when\n          loaded.\n        :param chooses_path: ``chooses_path(path)`` is called before\n          :paramref:`process` and returns :obj:`True` or :obj:`False`\n          depending on whether a specific path should be loaded and passed to\n          :paramref:`process`.\n        \"\"\"\n        self._process = process\n        self._chooses_path = chooses_path\n\n    def folder(self, folder):\n        \"\"\"Load all files from a folder recursively.\n\n        Depending on :meth:`chooses_path` some paths may not be loaded.\n        Every loaded path is processed and returned part of the returned list.\n\n        :param str folder: the folder to load the files from\n        :rtype: list\n        :return: a list of the results of the processing steps of the loaded\n          files\n        \"\"\"\n        result = []\n        for root, _, files in os.walk(folder):\n            for file in files:\n                path = os.path.join(root, file)\n                if self._chooses_path(path):\n                    result.append(self.path(path))\n        return result\n\n    def chooses_path(self, path):\n        \"\"\":return: whether the path should be loaded\n        :rtype: bool\n\n        :param str path: the path to the file to be tested\n        \"\"\"\n        return self._chooses_path(path)\n\n    def path(self, path):\n        \"\"\"load a :paramref:`path` and return the processed result\n\n        :param str path: the path to the file to be processed\n        :return: the result of processing step\n        \"\"\"\n        return self._process(path)\n\n    def _relative_to_absolute(self, module_location, folder):\n        \"\"\":return: the absolute path for the `folder` relative to\n        the module_location.\n        :rtype: str\n        \"\"\"\n        if os.path.isfile(module_location):\n            path = os.path.dirname(module_location)\n        elif os.path.isdir(module_location):\n            path = module_location\n        else:\n            module_folder = os.path.dirname(module_location)\n            if module_folder:\n                path = module_folder\n            else:\n                __import__(module_location)\n                module = sys.modules[module_location]\n                path = os.path.dirname(module.__file__)\n        absolute_path = os.path.join(path, folder)\n        return absolute_path\n\n    def relative_folder(self, module, folder):\n        \"\"\"Load a folder located relative to a module and return the processed\n        result.\n\n        :param str module: can be\n\n          - a path to a folder\n          - a path to a file\n          - a module name\n\n        :param str folder: the path of a folder relative to :paramref:`module`\n        :return: a list of the results of the processing\n        :rtype: list\n\n        Depending on :meth:`chooses_path` some paths may not be loaded.\n        Every loaded path is processed and returned part of the returned list.\n        You can use :meth:`choose_paths` to find out which paths are chosen to\n        load.\n        \"\"\"\n        folder = self._relative_to_absolute(module, folder)\n        return self.folder(folder)\n\n    def relative_file(self, module, file):\n        \"\"\"Load a file relative to a module.\n\n        :param str module: can be\n\n          - a path to a folder\n          - a path to a file\n          - a module name\n\n        :param str folder: the path of a folder relative to :paramref:`module`\n        :return: the result of the processing\n\n        \"\"\"\n        path = self._relative_to_absolute(module, file)\n        return self.path(path)\n\n    def choose_paths(self, paths):\n        \"\"\":return: the paths that are chosen by :meth:`chooses_path`\n        :rtype: list\n        \"\"\"\n        return [path for path in paths if self._chooses_path(path)]\n\n    def example(self, relative_path):\n        \"\"\"Load an example from the knitting pattern examples.\n\n        :param str relative_path: the path to load\n        :return: the result of the processing\n\n        You can use :meth:`knittingpattern.Loader.PathLoader.examples`\n        to find out the paths of all examples.\n        \"\"\"\n        example_path = os.path.join(\"examples\", relative_path)\n        return self.relative_file(__file__, example_path)\n\n    def examples(self):\n        \"\"\"Load all examples form the examples folder of this packge.\n\n        :return: a list of processed examples\n        :rtype: list\n\n        Depending on :meth:`chooses_path` some paths may not be loaded.\n        Every loaded path is processed and returned part of the returned list.\n        \"\"\"\n        return self.relative_folder(__file__, \"examples\")\n\n\nclass ContentLoader(PathLoader):\n    \"\"\"Load contents of files and ressources.\n\n    The :paramref:`process <PathLoader.__init__.process>` is called with a\n    :class:`string <str>` as first argument: ``process(string)``.\n    \"\"\"\n\n    def string(self, string):\n        \"\"\":return: the processed result of a string\n        :param str string: the string to load the ocntent from\n        \"\"\"\n        return self._process(string)\n\n    def file(self, file):\n        \"\"\":return: the processed result of the content of a file-like object.\n\n        :param file: the file-like object to load the content from.\n          It should support the ``read`` method.\n        \"\"\"\n        string = file.read()\n        return self.string(string)\n\n    def path(self, path):\n        \"\"\":return: the processed result of a :paramref:`path's <path>` content.\n        :param str path: the path where to load the content from.\n          It should exist on the local file system.\n        \"\"\"\n        with open(path) as file:\n            return self.file(file)\n\n    def url(self, url, encoding=\"UTF-8\"):\n        \"\"\"load and process the content behind a url\n\n        :return: the processed result of the :paramref:`url's <url>` content\n        :param str url: the url to retrieve the content from\n        :param str encoding: the encoding of the retrieved content.\n          The default encoding is UTF-8.\n\n        \"\"\"\n        import urllib.request\n        with urllib.request.urlopen(url) as file:\n            webpage_content = file.read()\n        webpage_content = webpage_content.decode(encoding)\n        return self.string(webpage_content)\n\n\nclass JSONLoader(ContentLoader):\n    \"\"\"Load an process JSON from various locations.\n\n    The :paramref:`process <PathLoader.__init__.process>` is called with an\n    :class:`object` as first argument: ``process(object)``.\n    \"\"\"\n\n    def object(self, object_):\n        \"\"\"Processes an already loaded object.\n\n        :return: the result of the processing step\n        :param object: the object to be loaded\n        \"\"\"\n        return self._process(object_)\n\n    def string(self, string):\n        \"\"\"Load an object from a string and return the processed JSON content\n\n        :return: the result of the processing step\n        :param str string: the string to load the JSON from\n        \"\"\"\n        object_ = json.loads(string)\n        return self.object(object_)\n\n\n__all__ = [\"JSONLoader\", \"ContentLoader\", \"PathLoader\", \"true\", \"identity\"]\n"
  },
  {
    "path": "knittingpattern/Mesh.py",
    "content": "\"\"\"This module contains the meshes of the knit work.\"\"\"\nfrom abc import ABCMeta, abstractmethod\n\n\nclass Mesh(metaclass=ABCMeta):\n\n    \"\"\"A mesh that is either consumed or produced by an instruction.\n\n    .. code:: python\n\n        assert mesh.is_produced() or mesh.is_consumed()\n\n    Since this is an abstract base class you will only get instances of\n    :class:`ProducedMesh <knittingpattern.Mesh.ProducedMesh>` and\n    :class:`ConsumedMesh <knittingpattern.Mesh.ConsumedMesh>`.\n\n    \"\"\"\n\n    @abstractmethod\n    def _producing_instruction_and_index(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _producing_row_and_index(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _consuming_instruction_and_index(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _consuming_row_and_index(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _is_produced(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _is_consumed(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _is_consumed_mesh(self):\n        \"\"\"Replace this method.\n\n        :return: whether this mesh is an instance of a ConsumedMesh.\n        \"\"\"\n\n    @abstractmethod\n    def _disconnect(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _connect_to(self, other_mesh):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _as_produced_mesh(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _as_consumed_mesh(self):\n        \"\"\"Replace this method.\"\"\"\n\n    @abstractmethod\n    def _is_connected_to(self, other_mesh):\n        \"\"\"Replace this method.\"\"\"\n\n    def _assert_is_produced(self):\n        assert self._is_produced(), \"Check with is_produced() before!\"\n\n    def _assert_is_consumed(self):\n        assert self._is_consumed(), \"Check with is_consumed() before!\"\n\n    def is_produced(self):\n        \"\"\"Whether the mesh has an instruction that produces it.\n\n        :return: whether the mesh is produced by an instruction\n        :rtype: bool\n\n        If you get this mesh from\n        :attr:`knittingpattern.Instruction.InstructionInRow.produced_meshes` or\n        :attr:`knittingpattern.Row.Row.produced_meshes`,\n        this should be :obj:`True`.\n\n        .. warning:: Before you use any methods on how the mesh is produced,\n          you should check with ``mesh.is_produced()``.\n        \"\"\"\n        return self._is_produced()\n\n    def is_consumed(self):\n        \"\"\"Whether the mesh has an instruction that consumed it.\n\n        :return: whether the mesh is consumed by an instruction\n        :rtype: bool\n\n        If you get this mesh from\n        :attr:`knittingpattern.Instruction.InstructionInRow.consumed_meshes` or\n        :attr:`knittingpattern.Row.Row.consumed_meshes`,\n        this should be :obj:`True`.\n\n        .. warning:: Before you use any methods on how the mesh is consumed,\n          you should check with ``mesh.is_consumed()``.\n        \"\"\"\n        return self._is_consumed()\n\n    @property\n    def index_in_producing_instruction(self):\n        \"\"\"Index in instruction as a produced mesh.\n\n        :return: the index of the mesh in the list of meshes that\n          :attr:`producing_instruction` produces\n        :rtype: int\n\n        .. code:: python\n\n            instruction = mesh.producing_instruction\n            index = mesh.index_in_producing_instruction\n            assert instruction.produced_meshes[index] == mesh\n\n        .. seealso:: :attr:`producing_instruction`,\n          :attr:`index_in_consuming_instruction`\n\n        .. warning:: Check with :meth:`is_produced` before!\n        \"\"\"\n        self._assert_is_produced()\n        return self._producing_instruction_and_index()[1]\n\n    @property\n    def producing_instruction(self):\n        \"\"\"Instruction which produces this mesh.\n\n        :return: the instruction that produces this mesh\n        :rtype: knittingpattern.Instruction.InstructionInRow\n\n        .. seealso:: :attr:`index_in_producing_instruction`,\n          :attr:`producing_row`, :attr:`consuming_row`\n\n        .. warning:: Check with :meth:`is_produced` before!\n        \"\"\"\n        self._assert_is_produced()\n        return self._producing_instruction_and_index()[0]\n\n    @property\n    def producing_row(self):\n        \"\"\"Row which produces this mesh.\n\n        :return: the row of the instruction that produces this mesh\n        :rtype: knittingpattern.Row.Row\n\n        .. seealso:: :attr:`index_in_producing_row`,\n          :attr:`producing_instruction`, :attr:`consuming_row`\n\n        .. warning:: Check with :meth:`is_produced` before!\n        \"\"\"\n        self._assert_is_produced()\n        return self._producing_row_and_index()[0]\n\n    @property\n    def index_in_producing_row(self):\n        \"\"\"Index in row as produced mesh.\n\n        :return: the index of the mesh in the :attr:`producing_row`\n        :rtype: int\n\n        .. code:: python\n\n            row = mesh.producing_row\n            index = mesh.index_in_producing_row\n            assert row[index] == mesh\n\n        .. seealso:: :attr:`producing_row`, :attr:`index_in_consuming_row`\n\n        .. warning:: Check with :meth:`is_produced` before!\n        \"\"\"\n        self._assert_is_produced()\n        return self._producing_row_and_index()[1]\n\n    @property\n    def index_in_consuming_row(self):\n        \"\"\"Index in row as consumed mesh.\n\n        :return: the index of the mesh in the list of meshes that\n          :attr:`consuming_row` consumes\n        :rtype: int\n\n        .. code:: python\n\n            row = mesh.consuming_row\n            index = mesh.index_in_consuming_row\n            assert row.consumed_meshes[index] == mesh\n\n        .. seealso:: :attr:`consuming_row`, :attr:`index_in_producing_row`\n\n        .. warning:: Check with :meth:`is_consumed` before!\n        \"\"\"\n        self._assert_is_consumed()\n        return self._consuming_row_and_index()[1]\n\n    @property\n    def consuming_row(self):\n        \"\"\"Row which consumes this mesh.\n\n        :return: the row that consumes this mesh\n        :rtype: knittingpattern.Row.Row\n\n        .. seealso:: :attr:`index_in_consuming_row`,\n          :attr:`consuming_instruction`, :attr:`producing_row`\n\n        .. warning:: Check with :meth:`is_consumed` before!\n        \"\"\"\n        self._assert_is_consumed()\n        return self._consuming_row_and_index()[0]\n\n    @property\n    def consuming_instruction(self):\n        \"\"\"Instruction which consumes this mesh.\n\n        :return: the instruction that consumes this mesh\n        :rtype: knittingpattern.Instruction.InstructionInRow\n\n        .. seealso:: :attr:`index_in_consuming_instruction`,\n          :attr:`consuming_row`, :attr:`producing_instruction`\n\n        .. warning:: Check with :meth:`is_consumed` before!\n        \"\"\"\n        self._assert_is_consumed()\n        return self._consuming_instruction_and_index()[0]\n\n    @property\n    def index_in_consuming_instruction(self):\n        \"\"\"Index in instruction as consumed mesh.\n\n        :return: the index of the mesh in the list of meshes that\n          :attr:`consuming_instruction` consumes\n        :rtype: int\n\n        .. code:: python\n\n            instruction = mesh.consuming_instruction\n            index = mesh.index_in_consuming_instruction\n            assert instruction.consumed_meshes[index] == mesh\n\n        .. seealso:: :attr:`consuming_instruction`,\n          :attr:`index_in_consuming_instruction`\n\n        .. warning:: Check with :meth:`is_consumed` before!\n        \"\"\"\n        self._assert_is_consumed()\n        return self._consuming_instruction_and_index()[1]\n\n    def is_knit(self):\n        \"\"\"Whether the mesh is produced by a knit instruction.\n\n        :return: whether the mesh is knit by an instruction\n        :rtype: bool\n\n        .. seealso:: :attr:`producing_instruction`\n        \"\"\"\n        self._assert_is_produced()\n        return self._producing_instruction_and_index()[0].does_knit()\n\n    def __repr__(self):\n        \"\"\"This mesh as string.\n\n        :return: the string representation of this mesh.\n        :rtype: str\n\n        This is useful for :func:`print` and class:`str`\n        \"\"\"\n        if self._is_consumed():\n            instruction, _ = self._consuming_instruction_and_index()\n            row, row_index = self._consuming_row_and_index()\n            consume_string = \" for {} in {}[{}]\".format(\n                instruction,\n                row,\n                row_index\n            )\n        else:\n            consume_string = \"\"\n        if self._is_produced():\n            instruction, _ = self._producing_instruction_and_index()\n            row, row_index = self._producing_row_and_index()\n            produce_string = \" by {} in {}[{}]\".format(\n                instruction,\n                row,\n                row_index\n            )\n        else:\n            produce_string = \"\"\n        return \"<{}{}{}>\".format(\n            self.__class__.__name__, produce_string, consume_string\n        )\n\n    def disconnect(self):\n        \"\"\"Remove the connection between two rows through this mesh.\n\n        After disconnecting this mesh, it can be connected anew.\n        \"\"\"\n        if self.is_connected():\n            self._disconnect()\n\n    def connect_to(self, other_mesh):\n        \"\"\"Create a connection to an other mesh.\n\n        .. warning:: Both meshes need to be disconnected and one needs to be\n          a consumed and the other a produced mesh. You can check if a\n          connection is possible using :meth:`can_connect_to`.\n\n        .. seealso:: :meth:`is_consumed`, :meth:`is_produced`,\n          :meth:`can_connect_to`\n        \"\"\"\n        other_mesh.disconnect()\n        self.disconnect()\n        self._connect_to(other_mesh)\n\n    def is_connected(self):\n        \"\"\"Returns whether this mesh is already connected.\n\n        :return: whether this mesh is connected to an other.\n        :rtype: bool\n        \"\"\"\n        return self._is_consumed() and self._is_produced()\n\n    def as_produced_mesh(self):\n        \"\"\"The produced part to this mesh.\n\n        If meshes are split up, it may be important which row the mesh is\n        connected to afterwards. This method returns the mesh that is\n        connected to the :attr:`producing row <producing_row>`.\n\n        If you got this mesh from :attr:`InstructionInRow.produced_meshes\n        <knittinpattern.Instruction.InstructionInRow.produced_meshes>` or\n        :attr:`Row.produced_meshes <knittinpattern.Row.Row.produced_meshes>`,\n        this returns the same object.\n\n        .. seealso:: :meth:`as_consumed_mesh`,\n          :attr:`knittinpattern.Instruction.InstructionInRow.produced_meshes`,\n          :attr:`knittinpattern.Row.Row.produced_meshes`\n        \"\"\"\n        self._assert_is_produced()\n        return self._as_produced_mesh()\n\n    def as_consumed_mesh(self):\n        \"\"\"The consumed part to this mesh.\"\"\"\n        self._assert_is_consumed()\n        return self._as_consumed_mesh()\n\n    def is_mesh(self):\n        \"\"\"Whether this object is a mesh.\n\n        :return: :obj:`True`\n        :rtype: bool\n        \"\"\"\n        return True\n\n    def is_connected_to(self, other_mesh):\n        \"\"\"Whether the one mesh is conencted to the other.\"\"\"\n        assert other_mesh.is_mesh()\n        return self._is_connected_to(other_mesh)\n\n    def can_connect_to(self, other):\n        \"\"\"Whether a connection can be established between those two meshes.\"\"\"\n        assert other.is_mesh()\n        disconnected = not other.is_connected() and not self.is_connected()\n        types_differ = self._is_consumed_mesh() != other._is_consumed_mesh()\n        return disconnected and types_differ\n\n\nclass ProducedMesh(Mesh):\n    \"\"\"A :class:`~knittingpattern.Mesh.Mesh` that has a producing instruction\n    \"\"\"\n\n    def __init__(self, producing_instruction,\n                 index_in_producing_instruction):\n        \"\"\"\n        :param producing_instruction: the\n          :class:`instruction <knittingpattern.Instruction.InstructionInRow>`\n          that produces the mesh\n        :param int index_in_producing_instruction: the index of the mesh\n          in the list of meshes that :attr:`producing_instruction`\n          produces\n\n        .. note:: There should be no necessity to create instances of this\n          directly. You should be able to use\n          ``instruction.produced_meshes`` or ``instruction.consumed_meshes``\n          to access the :class:`meshes <knittingpattern.Mesh.Mesh>`.\n\n        \"\"\"\n        self.__producing_instruction_and_index = (\n            producing_instruction,\n            index_in_producing_instruction\n        )\n        self._consumed_part = None\n\n    def _producing_instruction_and_index(self):\n        return self.__producing_instruction_and_index\n\n    def _producing_row_and_index(self):\n        instruction, index = self.__producing_instruction_and_index\n        producing_row = instruction.row\n        return (producing_row,\n                index + instruction.index_of_first_produced_mesh_in_row)\n\n    def _consuming_instruction_and_index(self):\n        return self._consumed_part._consuming_instruction_and_index()\n\n    def _consuming_row_and_index(self):\n        return self._consumed_part._consuming_row_and_index()\n\n    def _is_produced(self):\n        return True\n\n    def _is_consumed(self):\n        return self._consumed_part is not None\n\n    def _is_consumed_mesh(self):\n        return False\n\n    def _disconnect(self):\n        assert self._consumed_part is not None, \"Use is_consumed() before.\"\n        self._consumed_part._disconnected()\n        self._consumed_part = None\n\n    def _connect_to(self, other_mesh):\n        assert other_mesh._is_consumed_mesh()\n        self._consumed_part = other_mesh\n        self._consumed_part._connect_to_produced_mesh(self)\n\n    def _as_produced_mesh(self):\n        return self\n\n    def _as_consumed_mesh(self):\n        assert self._consumed_part is not None\n        return self._consumed_part\n\n    def _is_connected_to(self, other_mesh):\n        return other_mesh is not None and other_mesh == self._consumed_part\n\n\nclass ConsumedMesh(Mesh):\n    \"\"\"A mesh that is only consumed by an instruction\"\"\"\n\n    def __init__(self, consuming_instruction,\n                 index_in_consuming_instruction):\n        \"\"\"\n        :param consuming_instruction: the\n          :class:`instruction <knittingpattern.Instruction.InstructionInRow>`\n          that consumes the mesh\n        :param int index_in_consuming_instruction: the index of the mesh\n          in the list of meshes that :attr:`consuming_instruction`\n          consumes\n\n        .. note:: There should be no necessity to create instances of this\n          directly. You should be able to use\n          ``instruction.produced_meshes`` or ``instruction.consumed_meshes``\n          to access the :class:`meshes <knittingpattern.Mesh.Mesh>`.\n\n        \"\"\"\n        self.__consuming_instruction_and_index = (\n            consuming_instruction,\n            index_in_consuming_instruction\n        )\n        self._produced_part = None\n\n    def _producing_instruction_and_index(self):\n        return self._produced_part._producing_instruction_and_index()\n\n    def _producing_row_and_index(self):\n        return self._produced_part._producing_row_and_index()\n\n    def _consuming_instruction_and_index(self):\n        return self.__consuming_instruction_and_index\n\n    def _consuming_row_and_index(self):\n        instruction, index = self.__consuming_instruction_and_index\n        consuming_row = instruction.row\n        return (\n            consuming_row, index +\n            instruction.index_of_first_consumed_mesh_in_row)\n\n    def _is_produced(self):\n        return self._produced_part is not None\n\n    def _is_consumed(self):\n        return True\n\n    def _is_consumed_mesh(self):\n        return True\n\n    def _disconnect(self):\n        assert self._produced_part is not None\n        self._produced_part._disconnect()\n\n    def _disconnected(self):\n        self._produced_part = None\n\n    def _connect_to(self, other_mesh):\n        assert not other_mesh._is_consumed_mesh()\n        other_mesh._connect_to(self)\n\n    def _connect_to_produced_mesh(self, produced_mesh):\n        \"\"\"This is called after a connection has been established by the\n        produced mesh.\"\"\"\n        self._produced_part = produced_mesh\n\n    def _as_produced_mesh(self):\n        assert self._produced_part is not None\n        return self._produced_part\n\n    def _as_consumed_mesh(self):\n        return self\n\n    def _is_connected_to(self, other_mesh):\n        if other_mesh._is_consumed_mesh():\n            return False\n        return other_mesh is not self and other_mesh._is_connected_to(self)\n\n__all__ = [\"Mesh\", \"ProducedMesh\", \"ConsumedMesh\"]\n"
  },
  {
    "path": "knittingpattern/Parser.py",
    "content": "\"\"\"In this module you can find the parsing of knitting pattern structures.\"\"\"\n# attributes\n\nID = \"id\"  #: the id of a row, an instruction or a pattern\nNAME = \"name\"  #: the name of a row\nTYPE = \"type\"  #: the type of an instruction or the knitting pattern set\nVERSION = \"version\"  #: the version of a knitting pattern set\nINSTRUCTIONS = \"instructions\"  #: the instructions in a row\nSAME_AS = \"same as\"  #: pointer to a inherit from\nPATTERNS = \"patterns\"  #: the patterns in the knitting pattern set\nROWS = \"rows\"  #: the rows inside a pattern\nCONNECTIONS = \"connections\"  #: the connections in a pattern\nFROM = \"from\"  #: the position and row a connection comes from\nTO = \"to\"  #: the position and row a connection goes to\nSTART = \"start\"  #: the mesh index the connection starts at\n#: the default mesh index the connection starts at if none is given\nDEFAULT_START = 0\nMESHES = \"meshes\"  #: the number of meshes of a connection\nCOMMENT = \"comment\"  #: a comment of a row, an instruction, anything\n\n# constants\n\n#: the default type of the knitting pattern set\nKNITTING_PATTERN_TYPE = \"knitting pattern\"\n\n\nclass ParsingError(ValueError):\n\n    \"\"\"Mistake in the provided object to parse.\n\n    This Error is raised if there is an error during the parsing for\n    :class:`~knittingpattern.Parser.Parser`.\n    \"\"\"\n\n\nclass Parser(object):\n\n    \"\"\"Parses a knitting pattern set and anything in it.\"\"\"\n\n    def __init__(self, specification):\n        \"\"\"Create a parser with a specification.\n\n        :param specification: the types and classes to use for the resulting\n          object structure, preferably a\n          :class:`knittingpattern.ParsingSpecification.ParsingSpecification`\n\n        \"\"\"\n        self._spec = specification\n        self._start()\n\n    def _start(self):\n        \"\"\"Initialize the parsing process.\"\"\"\n        self._instruction_library = self._spec.new_default_instructions()\n        self._as_instruction = self._instruction_library.as_instruction\n        self._id_cache = {}\n        self._pattern_set = None\n        self._inheritance_todos = []\n        self._instruction_todos = []\n\n    @staticmethod\n    def _to_id(id_):\n        \"\"\"Converts the argument to a object suitable as an identifier.\n\n        :return: a hashable object\n        \"\"\"\n        return tuple(id_) if isinstance(id_, list) else id_\n\n    def _error(self, text):\n        \"\"\"Raise an error.\n\n        :raises: a specified ParsingError\n        :param str text: the text to include in the error message\n        \"\"\"\n        raise self._spec.new_parsing_error(text)\n\n    def knitting_pattern_set(self, values):\n        \"\"\"Parse a knitting pattern set.\n\n        :param dict value: the specification of the knitting pattern set\n        :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n        :raises knittingpattern.KnittingPatternSet.ParsingError: if\n          :paramref:`value` does not fulfill the :ref:`specification\n          <FileFormatSpecification>`.\n\n        \"\"\"\n        self._start()\n        pattern_collection = self._new_pattern_collection()\n        self._fill_pattern_collection(pattern_collection, values)\n        self._create_pattern_set(pattern_collection, values)\n        return self._pattern_set\n\n    def _finish_inheritance(self):\n        \"\"\"Finish those who still need to inherit.\"\"\"\n        while self._inheritance_todos:\n            prototype, parent_id = self._inheritance_todos.pop()\n            parent = self._id_cache[parent_id]\n            prototype.inherit_from(parent)\n\n    def _delay_inheritance(self, prototype, parent_id):\n        \"\"\"Add a deleyed inheritance that is ti be resolved later.\n\n        When calling :meth:`_finish_inheritance` this inheritance chain shall\n        be resolved.\n        \"\"\"\n        self._inheritance_todos.append((prototype, parent_id))\n\n    def _finish_instructions(self):\n        \"\"\"Finish those who still need to inherit.\"\"\"\n        while self._instruction_todos:\n            row = self._instruction_todos.pop()\n            instructions = row.get(INSTRUCTIONS, [])\n            row.instructions.extend(instructions)\n\n    def _delay_instructions(self, row):\n        \"\"\"Add a deleyed inheritance that is ti be resolved later.\n\n        When calling :meth:`_finish_instructions` this inheritance chain shall\n        be resolved.\n        \"\"\"\n        self._instruction_todos.append(row)\n\n    def _new_pattern_collection(self):\n        \"\"\"Create a new pattern collection.\n\n        :return: a new specified pattern collection for\n          :meth:`knitting_pattern_set`\n        \"\"\"\n        return self._spec.new_pattern_collection()\n\n    def new_row_collection(self):\n        \"\"\"Create a new row collection.\n\n        :return: a new specified row collection for the\n          :meth:`knitting pattern <new_pattern>`\n        \"\"\"\n        return self._spec.new_row_collection()\n\n    def _fill_pattern_collection(self, pattern_collection, values):\n        \"\"\"Fill a pattern collection.\"\"\"\n        pattern = values.get(PATTERNS, [])\n        for pattern_to_parse in pattern:\n            parsed_pattern = self._pattern(pattern_to_parse)\n            pattern_collection.append(parsed_pattern)\n\n    def _row(self, values):\n        \"\"\"Parse a row.\"\"\"\n        row_id = self._to_id(values[ID])\n        row = self._spec.new_row(row_id, values, self)\n        if SAME_AS in values:\n            self._delay_inheritance(row, self._to_id(values[SAME_AS]))\n        self._delay_instructions(row)\n        self._id_cache[row_id] = row\n        return row\n\n    def new_row(self, id_):\n        \"\"\"Create a new row with an id.\n\n        :param id_: the id of the row\n        :return: a row\n        :rtype: knittingpattern.Row.Row\n        \"\"\"\n        return self._spec.new_row(id_, {}, self)\n\n    def instruction_in_row(self, row, specification):\n        \"\"\"Parse an instruction.\n\n        :param row: the row of the instruction\n        :param specification: the specification of the instruction\n        :return: the instruction in the row\n        \"\"\"\n        whole_instruction_ = self._as_instruction(specification)\n        return self._spec.new_instruction_in_row(row, whole_instruction_)\n\n    def _pattern(self, base):\n        \"\"\"Parse a pattern.\"\"\"\n        rows = self._rows(base.get(ROWS, []))\n        self._finish_inheritance()\n        self._finish_instructions()\n        self._connect_rows(base.get(CONNECTIONS, []))\n        id_ = self._to_id(base[ID])\n        name = base[NAME]\n        return self.new_pattern(id_, name, rows)\n\n    def new_pattern(self, id_, name, rows=None):\n        \"\"\"Create a new knitting pattern.\n\n        If rows is :obj:`None` it is replaced with the\n        :meth:`new_row_collection`.\n        \"\"\"\n        if rows is None:\n            rows = self.new_row_collection()\n        return self._spec.new_pattern(id_, name, rows, self)\n\n    def _rows(self, spec):\n        \"\"\"Parse a collection of rows.\"\"\"\n        rows = self.new_row_collection()\n        for row in spec:\n            rows.append(self._row(row))\n        return rows\n\n    def _connect_rows(self, connections):\n        \"\"\"Connect the parsed rows.\"\"\"\n        for connection in connections:\n            from_row_id = self._to_id(connection[FROM][ID])\n            from_row = self._id_cache[from_row_id]\n            from_row_start_index = connection[FROM].get(START, DEFAULT_START)\n            from_row_number_of_possible_meshes = \\\n                from_row.number_of_produced_meshes - from_row_start_index\n            to_row_id = self._to_id(connection[TO][ID])\n            to_row = self._id_cache[to_row_id]\n            to_row_start_index = connection[TO].get(START, DEFAULT_START)\n            to_row_number_of_possible_meshes = \\\n                to_row.number_of_consumed_meshes - to_row_start_index\n            meshes = min(from_row_number_of_possible_meshes,\n                         to_row_number_of_possible_meshes)\n            # TODO: test all kinds of connections\n            number_of_meshes = connection.get(MESHES, meshes)\n            from_row_stop_index = from_row_start_index + number_of_meshes\n            to_row_stop_index = to_row_start_index + number_of_meshes\n            assert 0 <= from_row_start_index <= from_row_stop_index\n            produced_meshes = from_row.produced_meshes[\n                from_row_start_index:from_row_stop_index]\n            assert 0 <= to_row_start_index <= to_row_stop_index\n            consumed_meshes = to_row.consumed_meshes[\n                to_row_start_index:to_row_stop_index]\n            assert len(produced_meshes) == len(consumed_meshes)\n            mesh_pairs = zip(produced_meshes, consumed_meshes)\n            for produced_mesh, consumed_mesh in mesh_pairs:\n                produced_mesh.connect_to(consumed_mesh)\n\n    def _get_type(self, values):\n        \"\"\":return: the type of a knitting pattern set.\"\"\"\n        if TYPE not in values:\n            self._error(\"No pattern type given but should be \"\n                        \"\\\"{}\\\"\".format(KNITTING_PATTERN_TYPE))\n        type_ = values[TYPE]\n        if type_ != KNITTING_PATTERN_TYPE:\n            self._error(\"Wrong pattern type. Type is \\\"{}\\\" \"\n                        \"but should be \\\"{}\\\"\"\n                        \"\".format(type_, KNITTING_PATTERN_TYPE))\n        return type_\n\n    def _get_version(self, values):\n        \"\"\":return: the version of :paramref:`values`.\"\"\"\n        return values[VERSION]\n\n    def _create_pattern_set(self, pattern, values):\n        \"\"\"Create a new pattern set.\"\"\"\n        type_ = self._get_type(values)\n        version = self._get_version(values)\n        comment = values.get(COMMENT)\n        self._pattern_set = self._spec.new_pattern_set(\n            type_, version, pattern, self, comment\n        )\n\n\ndef default_parser():\n    \"\"\"The parser with a default specification.\n\n    :return: a parser using a\n      :class:`knittingpattern.ParsingSpecification.DefaultSpecification`\n    :rtype: knittingpattern.Parser.Parser\n    \"\"\"\n    from .ParsingSpecification import DefaultSpecification\n    specification = DefaultSpecification()\n    return Parser(specification)\n\n\n__all__ = [\"Parser\", \"ID\", \"NAME\", \"TYPE\", \"VERSION\", \"INSTRUCTIONS\",\n           \"SAME_AS\", \"PATTERNS\", \"ROWS\", \"CONNECTIONS\", \"FROM\", \"TO\", \"START\",\n           \"DEFAULT_START\", \"MESHES\", \"COMMENT\", \"ParsingError\",\n           \"default_parser\"]\n"
  },
  {
    "path": "knittingpattern/ParsingSpecification.py",
    "content": "\"\"\"This modules specifies how to convert JSON to knitting patterns.\n\nWhen parsing :class:`knitting patterns\n<knittingpattern.KnittingPatternSet.KnittingPatternSet>` a lot of classes can\nbe used.\n\nThe :class:`ParsingSpecification` is the one place where to go to change a\nclass that is used throughout the whole structure loaded by e.g. a\n:class:`knittingpattern.Parser.Parser`.\n:func:`new_knitting_pattern_set_loader` is a convinient interface for\nloading knitting patterns.\n\nThese functions should do the same:\n\n.. code:: python\n\n    # (1) load from module\n    import knittingpattern\n    kp = knittingpattern.load_from_file(\"my_pattern\")\n\n    # (2) load from knitting pattern\n    from knittingpattern.ParsingSpecification import *\n    kp = new_knitting_pattern_set_loader().file(\"my_pattern\")\n\n\"\"\"\nfrom .Loader import JSONLoader\nfrom .Parser import Parser, ParsingError\nfrom .KnittingPatternSet import KnittingPatternSet\nfrom .IdCollection import IdCollection\nfrom .KnittingPattern import KnittingPattern\nfrom .Row import Row\nfrom .InstructionLibrary import DefaultInstructions\nfrom .Instruction import InstructionInRow\n\n\nclass ParsingSpecification(object):\n\n    \"\"\"This is the specification for knitting pattern parsers.\n\n    The :class:`<knittingpattern.Parser.Parser>` uses this specification\n    to parse the knitting patterns. You can change every class in the data\n    structure to add own functionality.\n    \"\"\"\n\n    def __init__(self,\n                 new_loader=JSONLoader,\n                 new_parser=Parser,\n                 new_parsing_error=ParsingError,\n                 new_pattern_set=KnittingPatternSet,\n                 new_pattern_collection=IdCollection,\n                 new_row_collection=IdCollection,\n                 new_pattern=KnittingPattern,\n                 new_row=Row,\n                 new_default_instructions=DefaultInstructions,\n                 new_instruction_in_row=InstructionInRow):\n        \"\"\"Create a new parsing specification.\"\"\"\n        self.new_loader = new_loader\n        self.new_parser = new_parser\n        self.new_parsing_error = new_parsing_error\n        self.new_pattern_set = new_pattern_set\n        self.new_pattern_collection = new_pattern_collection\n        self.new_row_collection = new_row_collection\n        self.new_pattern = new_pattern\n        self.new_row = new_row\n        self.new_default_instructions = new_default_instructions\n        self.new_instruction_in_row = new_instruction_in_row\n\n\nclass DefaultSpecification(ParsingSpecification):\n\n    \"\"\"This is the default specification.\n\n    It is created like pasing no arguments to :class:`ParsingSpecification`.\n    The idea is to make the default specification easy to spot and create.\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the default specification with no arguments.\"\"\"\n        super().__init__()\n\n    @classmethod\n    def __repr__(cls):\n        \"\"\"The string representation of the object.\n\n        :return: the string representation\n        :rtype: str\n        \"\"\"\n        return \"<{}.{}>\".format(cls.__module__, cls.__qualname__)\n\n\ndef new_knitting_pattern_set_loader(specification=DefaultSpecification()):\n    \"\"\"Create a loader for a knitting pattern set.\n\n    :param specification: a :class:`specification\n      <knittingpattern.ParsingSpecification.ParsingSpecification>`\n      for the knitting pattern set, default\n      :class:`DefaultSpecification`\n    \"\"\"\n    parser = specification.new_parser(specification)\n    loader = specification.new_loader(parser.knitting_pattern_set)\n    return loader\n\n\n__all__ = [\"ParsingSpecification\", \"new_knitting_pattern_set_loader\",\n           \"DefaultSpecification\"]\n"
  },
  {
    "path": "knittingpattern/Prototype.py",
    "content": "\"\"\"This module contains the :class:`~knittingpattern.Prototype.Prototype`\nthat can be used to create inheritance on object level instead of class level.\n\"\"\"\n\n\nclass Prototype(object):\n    \"\"\"This class provides inheritance of its specifications on object level.\n\n    .. _prototype-key:\n\n    Throughout this class `specification key` refers to a\n    :func:`hashable <hash>` object\n    to look up a value in the specification.\n    \"\"\"\n\n    def __init__(self, specification, inherited_values=()):\n        \"\"\"create a new prototype\n\n        :param specification: the specification of the prototype.\n          This specification can be inherited by other prototypes.\n          It can be a :class:`dict` or an other\n          :class:`knittingpattern.Prototype.Prototype` or anything else that\n          supports :meth:`__contains__` and :meth:`__getitem__`\n\n        To look up a key in the specification it will be walked through\n\n        1. :paramref:`specification`\n        2. :paramref:`inherited_values` in order\n\n        However, new lookups can be inserted at before\n        :paramref:`inherited_values`, by calling :meth:`inherit_from`.\n\n        \"\"\"\n        self.__specification = [specification] + list(inherited_values)\n\n    def get(self, key, default=None):\n        \"\"\"\n        :return: the value behind :paramref:`key` in the specification.\n          If no value was found, :paramref:`default` is returned.\n        :param key: a :ref:`specification key <prototype-key>`\n        \"\"\"\n        for base in self.__specification:\n            if key in base:\n                return base[key]\n        return default\n\n    def __getitem__(self, key):\n        \"\"\"``prototype[key]``\n\n        :param key: a :ref:`specification key <prototype-key>`\n        :return: the value behind :paramref:`key` in the specification\n        :raises KeyError: if no value was found\n\n        \"\"\"\n        default = []\n        value = self.get(key, default)\n        if value is default:\n            raise KeyError(key)\n        return value\n\n    def __contains__(self, key):\n        \"\"\"``key in prototype``\n\n        :param key: a :ref:`specification key <prototype-key>`\n        :return: whether the key was found in the specification\n        :rtype: bool\n\n        \"\"\"\n        default = []\n        value = self.get(key, default)\n        return value is not default\n\n    def inherit_from(self, new_specification):\n        \"\"\"Inherit from a :paramref:`new_specification`\n\n        :param new_specification: a specification as passed to :meth:`__init__`\n\n        The :paramref:`new_specification` is inserted before the first\n        :paramref:`inherited value <__init__.inherited_values>`.\n\n        If the order is\n\n        1. :paramref:`~__init__.specification`\n        2. :paramref:`~__init__.inherited_values`\n\n        after calling ``prototype.inherit_from(new_specification)`` the lookup\n        order is\n\n        1. :paramref:`~__init__.specification`\n        2. :paramref:`new_specification`\n        3. :paramref:`~__init__.inherited_values`\n\n        \"\"\"\n        self.__specification.insert(1, new_specification)\n\n\n__all__ = [\"Prototype\"]\n"
  },
  {
    "path": "knittingpattern/Row.py",
    "content": "\"\"\"This module contains the rows of instructions of knitting patterns.\n\nThe :class:`rows <Row>` are part of :class:`knitting patterns\n<knittingpattern.KnittingPattern.KnittingPattern>`.\nThey contain :class:`instructions\n<knittingpattern.Instruction.InstructionInRow>` and can be connected to other\nrows.\n\"\"\"\nfrom .Prototype import Prototype\nfrom itertools import chain\nfrom ObservableList import ObservableList\nfrom .utils import unique\n\nCOLOR = \"color\"  #: the color of the row\n\n#: an error message\nCONISTENCY_MESSAGE = \"The data structure must be consistent.\"\n\n\nclass Row(Prototype):\n\n    \"\"\"This class contains the functionality for rows.\n\n    This class is used by :class:`knitting patterns\n    <knittingpattern.KnittingPattern.KnittingPattern>`.\n    \"\"\"\n\n    def __init__(self, row_id, values, parser):\n        \"\"\"Create a new row.\n\n        :param row_id: an identifier for the row\n        :param values: the values from the specification\n        :param list inheriting_from: a list of specifications to inherit values\n          from, see :class:`knittingpattern.Prototype.Prototype`\n\n        .. note:: Seldomly, you need to create this row on your own. You can\n          load it with the :mod:`knittingpattern` or the\n          :class:`knittingpattern.Parser.Parser`.\n        \"\"\"\n        super().__init__(values)\n        self._id = row_id\n        self._instructions = ObservableList()\n        self._instructions.register_observer(self._instructions_changed)\n        self._parser = parser\n\n    def _instructions_changed(self, change):\n        \"\"\"Call when there is a change in the instructions.\"\"\"\n        if change.adds():\n            for index, instruction in change.items():\n                if isinstance(instruction, dict):\n                    in_row = self._parser.instruction_in_row(self, instruction)\n                    self.instructions[index] = in_row\n                else:\n                    instruction.transfer_to_row(self)\n\n    @property\n    def id(self):\n        \"\"\"The id of the row.\n\n        :return: the id of the row\n        \"\"\"\n        return self._id\n\n    @property\n    def instructions(self):\n        \"\"\"The instructions in this row.\n\n        :return: a collection of :class:`instructions inside the row\n          <knittingpattern.Instruction.InstructionInRow>`\n        :rtype: ObservableList.ObservableList\n        \"\"\"\n        return self._instructions\n\n    @property\n    def number_of_produced_meshes(self):\n        \"\"\"The number of meshes that this row produces.\n\n        :return: the number of meshes that this row produces\n        :rtype: int\n\n        .. seealso::\n          :meth:`Instruction.number_of_produced_meshes()\n          <knittingpattern.Instruction.Instruction.number_of_produced_meshes>`,\n          :meth:`number_of_consumed_meshes`\n        \"\"\"\n        return sum(instruction.number_of_produced_meshes\n                   for instruction in self.instructions)\n\n    @property\n    def number_of_consumed_meshes(self):\n        \"\"\"The number of meshes that this row consumes.\n\n        :return: the number of meshes that this row consumes\n        :rtype: int\n\n        .. seealso::\n          :meth:`Instruction.number_of_consumed_meshes()\n          <knittingpattern.Instruction.Instruction.number_of_consumed_meshes>`,\n          :meth:`number_of_produced_meshes`\n        \"\"\"\n        return sum(instruction.number_of_consumed_meshes\n                   for instruction in self.instructions)\n\n    @property\n    def produced_meshes(self):\n        \"\"\"The meshes that this row produces with its instructions.\n\n        :return: a collection of :class:`meshes <knittingpattern.Mesh.Mesh>`\n          that this instruction produces\n\n        \"\"\"\n        return list(chain(*(instruction.produced_meshes\n                            for instruction in self.instructions)))\n\n    @property\n    def consumed_meshes(self):\n        \"\"\"Same as :attr:`produced_meshes` but for consumed meshes.\"\"\"\n        return list(chain(*(instruction.consumed_meshes\n                            for instruction in self.instructions)))\n\n    def __repr__(self):\n        \"\"\"The string representation of this row.\n\n        :return: a string representation of this row\n        :rtype: str\n        \"\"\"\n        return \"<{} {}>\".format(self.__class__.__qualname__, self.id)\n\n    @property\n    def color(self):\n        \"\"\"The color of the row.\n\n        :return: the color of the row as specified or :obj:`None`\n        \"\"\"\n        return self.get(COLOR)\n\n    @property\n    def instruction_colors(self):\n        \"\"\"The colors of the instructions in the row in the order tehy appear.\n\n        :return: a list of colors of the knitting pattern in the order that\n          they appear in\n        :rtype: list\n        \"\"\"\n        return unique(instruction.colors for instruction in self.instructions)\n\n    @property\n    def last_produced_mesh(self):\n        \"\"\"The last produced mesh.\n\n        :return: the last produced mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is produced\n\n        .. seealso:: :attr:`number_of_produced_meshes`\n        \"\"\"\n        for instruction in reversed(self.instructions):\n            if instruction.produces_meshes():\n                return instruction.last_produced_mesh\n        raise IndexError(\"{} produces no meshes\".format(self))\n\n    @property\n    def last_consumed_mesh(self):\n        \"\"\"The last consumed mesh.\n\n        :return: the last consumed mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is consumed\n\n        .. seealso:: :attr:`number_of_consumed_meshes`\n        \"\"\"\n        for instruction in reversed(self.instructions):\n            if instruction.consumes_meshes():\n                return instruction.last_consumed_mesh\n        raise IndexError(\"{} consumes no meshes\".format(self))\n\n    @property\n    def first_produced_mesh(self):\n        \"\"\"The first produced mesh.\n\n        :return: the first produced mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is produced\n\n        .. seealso:: :attr:`number_of_produced_meshes`\n        \"\"\"\n        for instruction in self.instructions:\n            if instruction.produces_meshes():\n                return instruction.first_produced_mesh\n        raise IndexError(\"{} produces no meshes\".format(self))\n\n    @property\n    def first_consumed_mesh(self):\n        \"\"\"The first consumed mesh.\n\n        :return: the first consumed mesh\n        :rtype: knittingpattern.Mesh.Mesh\n        :raises IndexError: if no mesh is consumed\n\n        .. seealso:: :attr:`number_of_consumed_meshes`\n        \"\"\"\n        for instruction in self.instructions:\n            if instruction.consumes_meshes():\n                return instruction.first_consumed_mesh\n        raise IndexError(\"{} consumes no meshes\".format(self))\n\n    @property\n    def rows_before(self):\n        \"\"\"The rows that produce meshes for this row.\n\n        :rtype: list\n        :return: a list of rows that produce meshes for this row. Each row\n          occurs only once. They are sorted by the first occurrence in the\n          instructions.\n        \"\"\"\n        rows_before = []\n        for mesh in self.consumed_meshes:\n            if mesh.is_produced():\n                row = mesh.producing_row\n                if rows_before not in rows_before:\n                    rows_before.append(row)\n        return rows_before\n\n    @property\n    def rows_after(self):\n        \"\"\"The rows that consume meshes from this row.\n\n        :rtype: list\n        :return: a list of rows that consume meshes from this row. Each row\n          occurs only once. They are sorted by the first occurrence in the\n          instructions.\n        \"\"\"\n        rows_after = []\n        for mesh in self.produced_meshes:\n            if mesh.is_consumed():\n                row = mesh.consuming_row\n                if rows_after not in rows_after:\n                    rows_after.append(row)\n        return rows_after\n\n    @property\n    def first_instruction(self):\n        \"\"\"The first instruction of the rows instructions.\n\n        :rtype: knittingpattern.Instruction.InstructionInRow\n        :return: the first instruction in this row's :attr:`instructions`\n        \"\"\"\n        return self.instructions[0]\n\n    @property\n    def last_instruction(self):\n        \"\"\"The last instruction of the rows instructions.\n\n        :rtype: knittingpattern.Instruction.InstructionInRow\n        :return: the last instruction in this row's :attr:`instructions`\n        \"\"\"\n        return self.instructions[-1]\n\n__all__ = [\"Row\", \"COLOR\"]\n"
  },
  {
    "path": "knittingpattern/__init__.py",
    "content": "\"\"\"The knitting pattern module.\n\nLoad and convert knitting patterns using the convenience functions listed\nbelow.\n\"\"\"\n# there should be no imports\n\n#: the version of the knitting pattern library\n__version__ = '0.1.19'\n\n#: an empty knitting pattern set as specification\nEMPTY_KNITTING_PATTERN_SET = {\"version\": \"0.1\", \"type\": \"knitting pattern\",\n                              \"patterns\": []}\n\n\ndef load_from():\n    \"\"\"Create a loader to load knitting patterns with.\n\n    :return: the loader to load objects with\n    :rtype: knittingpattern.Loader.JSONLoader\n\n    Example:\n\n    .. code:: python\n\n       import knittingpattern, webbrowser\n       k = knittingpattern.load_from().example(\"Cafe.json\")\n       webbrowser.open(k.to_svg(25).temporary_path(\".svg\"))\n\n    \"\"\"\n    from .ParsingSpecification import new_knitting_pattern_set_loader\n    return new_knitting_pattern_set_loader()\n\n\ndef load_from_object(object_):\n    \"\"\"Load a knitting pattern from an object.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    \"\"\"\n    return load_from().object(object_)\n\n\ndef load_from_string(string):\n    \"\"\"Load a knitting pattern from a string.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    \"\"\"\n    return load_from().string(string)\n\n\ndef load_from_file(file):\n    \"\"\"Load a knitting pattern from a file-like object.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    \"\"\"\n    return load_from().file(file)\n\n\ndef load_from_path(path):\n    \"\"\"Load a knitting pattern from a file behind located at `path`.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    \"\"\"\n    return load_from().path(path)\n\n\ndef load_from_url(url):\n    \"\"\"Load a knitting pattern from a url.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    \"\"\"\n    return load_from().url(url)\n\n\ndef load_from_relative_file(module, path_relative_to):\n    \"\"\"Load a knitting pattern from a path relative to a module.\n\n    :param str module: can be a module's file, a module's name or\n      a module's path.\n    :param str path_relative_to: is the path relative to the modules location.\n      The result is loaded from this.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    \"\"\"\n    return load_from().relative_file(module, path_relative_to)\n\n\ndef convert_from_image(colors=(\"white\", \"black\")):\n    \"\"\"Convert and image to a knitting pattern.\n\n    :return: a loader\n    :rtype: knittingpattern.Loader.PathLoader\n    :param tuple colors: the colors to convert to\n\n    .. code:: python\n\n        convert_from_image().path(\"pattern.png\").path(\"pattern.json\")\n        convert_from_image().path(\"pattern.png\").knitting_pattern()\n\n    .. seealso:: :mod:`knittingoattern.convert.image_to_knitting_pattern`\n    \"\"\"\n    from .convert.image_to_knittingpattern import \\\n        convert_image_to_knitting_pattern\n    return convert_image_to_knitting_pattern(colors=colors)\n\n\ndef new_knitting_pattern(id_, name=None):\n    \"\"\"Create a new knitting pattern.\n\n    :return: a new empty knitting pattern.\n    :param id_: the id of the knitting pattern\n    :param name: the name of the knitting pattern or :obj:`None` if the\n      :paramref:`id_` should be used\n    :rtype: knittingpattern.KnittingPattern.KnittingPattern\n\n    .. seealso:: :meth:`KnittingPatternSet.add_new_pattern()\n      <knittingpattern.KnittingPatternSet.KnittingPatternSet.add_new_pattern>`\n    \"\"\"\n    knitting_pattern_set = new_knitting_pattern_set()\n    return knitting_pattern_set.add_new_pattern(id_, name)\n\n\ndef new_knitting_pattern_set():\n    \"\"\"Create a new, empty knitting pattern set.\n\n    :rtype: knittingpattern.KnittingPatternSet.KnittingPatternSet\n    :return: a new, empty knitting pattern set\n    \"\"\"\n    return load_from_object(EMPTY_KNITTING_PATTERN_SET)\n\n__all__ = [\"load_from_object\", \"load_from_string\", \"load_from_file\",\n           \"load_from_path\", \"load_from_url\", \"load_from_relative_file\",\n           \"convert_from_image\", \"load_from\", \"new_knitting_pattern\",\n           \"new_knitting_pattern_set\"]\n"
  },
  {
    "path": "knittingpattern/convert/AYABPNGBuilder.py",
    "content": "\"\"\"Convert knitting patterns to png files.\n\nThese png files are used to be fed into the ayab-desktop software.\nThey only contain which meshes will be knit with a contrast color.\nThey just contain colors.\n\"\"\"\nimport webcolors\nimport PIL.Image\nfrom .color import convert_color_to_rrggbb\n\n\nclass AYABPNGBuilder(object):\n    \"\"\"Convert knitting patterns to png files that only contain the color\n    information and ``(x, y)`` coordinates.\n\n    .. _png-color:\n\n    Througout this class the term `color` refers to either\n\n    - a valid html5 color name such as ``\"black\"``, ``\"white\"``\n    - colors of the form ``\"#RGB\"``, ``\"#RRGGBB\"`` and ``\"#RRRGGGBBB\"``\n\n    \"\"\"\n\n    def __init__(self, min_x, min_y, max_x, max_y,\n                 default_color=\"white\"):\n        \"\"\"Initialize the builder with the bounding box and a default color.\n\n        .. _png-builder-bounds:\n\n        ``min_x <= x < max_x`` and ``min_y <= y < max_y`` are the bounds of the\n        instructions.\n        Instructions outside the bounds are not rendered.\n        Any Pixel that is not set has the :paramref:`default_color`.\n\n        :param int min_x: the lower bound of the x coordinates\n        :param int max_x: the upper bound of the x coordinates\n        :param int min_y: the lower bound of the y coordinates\n        :param int max_y: the upper bound of the y coordinates\n        :param default_color: a valid :ref:`color <png-color>`\n        \"\"\"\n        self._min_x = min_x\n        self._min_y = min_y\n        self._max_x = max_x\n        self._max_y = max_y\n        self._default_color = default_color\n        self._image = PIL.Image.new(\n            \"RGB\", (max_x - min_x, max_y - min_y),\n            self._convert_to_image_color(default_color))\n\n    def write_to_file(self, file):\n        \"\"\"write the png to the file\n\n        :param file: a file-like object\n        \"\"\"\n        self._image.save(file, format=\"PNG\")\n\n    @staticmethod\n    def _convert_color_to_rrggbb(color):\n        \"\"\"takes a :ref:`color <png-color>` and converts it into a 24 bit\n        color \"#RRGGBB\"\n\n        \"\"\"\n        return convert_color_to_rrggbb(color)\n\n    def _convert_rrggbb_to_image_color(self, rrggbb):\n        \"\"\":return: the color that is used by the image\"\"\"\n        return webcolors.hex_to_rgb(rrggbb)\n\n    def _convert_to_image_color(self, color):\n        \"\"\":return: a color that can be used by the image\"\"\"\n        rgb = self._convert_color_to_rrggbb(color)\n        return self._convert_rrggbb_to_image_color(rgb)\n\n    def _set_pixel_and_convert_color(self, x, y, color):\n        \"\"\"set the pixel but convert the color before.\"\"\"\n        if color is None:\n            return\n        color = self._convert_color_to_rrggbb(color)\n        self._set_pixel(x, y, color)\n\n    def _set_pixel(self, x, y, color):\n        \"\"\"set the color of the pixel.\n\n        :param color: must be a valid color in the form of \"#RRGGBB\".\n          If you need to convert color, use `_set_pixel_and_convert_color()`.\n        \"\"\"\n        if not self.is_in_bounds(x, y):\n            return\n        rgb = self._convert_rrggbb_to_image_color(color)\n        x -= self._min_x\n        y -= self._min_y\n        self._image.putpixel((x, y), rgb)\n\n    def set_pixel(self, x, y, color):\n        \"\"\"set the pixel at ``(x, y)`` position to :paramref:`color`\n\n        If ``(x, y)`` is out of the :ref:`bounds <png-builder-bounds>`\n        this does not change the image.\n\n        .. seealso:: :meth:`set_color_in_grid`\n        \"\"\"\n        self._set_pixel_and_convert_color(x, y, color)\n\n    def is_in_bounds(self, x, y):\n        \"\"\"\n        :return: whether ``(x, y)`` is inside the :ref:`bounds\n          <png-builder-bounds>`\n        :rtype: bool\n        \"\"\"\n        lower = self._min_x <= x and self._min_y <= y\n        upper = self._max_x > x and self._max_y > y\n        return lower and upper\n\n    def set_color_in_grid(self, color_in_grid):\n        \"\"\"Set the pixel at the position of the :paramref:`color_in_grid`\n        to its color.\n\n        :param color_in_grid: must have the following attributes:\n\n          - ``color`` is the :ref:`color <png-color>` to set the pixel to\n          - ``x`` is the x position of the pixel\n          - ``y`` is the y position of the pixel\n\n        .. seealso:: :meth:`set_pixel`, :meth:`set_colors_in_grid`\n        \"\"\"\n        self._set_pixel_and_convert_color(\n            color_in_grid.x, color_in_grid.y, color_in_grid.color)\n\n    def set_colors_in_grid(self, some_colors_in_grid):\n        \"\"\"Same as :meth:`set_color_in_grid` but with a collection of\n        colors in grid.\n\n        :param iterable some_colors_in_grid: a collection of colors in grid for\n          :meth:`set_color_in_grid`\n        \"\"\"\n        for color_in_grid in some_colors_in_grid:\n            self._set_pixel_and_convert_color(\n                color_in_grid.x, color_in_grid.y, color_in_grid.color)\n\n    @property\n    def default_color(self):\n        \"\"\":return: the :ref:`color <png-color>` of the pixels that are not set\n\n        You can set this color by passing it to the :meth:`constructor\n        <__init__>`.\n        \"\"\"\n        return self._default_color\n\n\n__all__ = [\"AYABPNGBuilder\"]\n"
  },
  {
    "path": "knittingpattern/convert/AYABPNGDumper.py",
    "content": "\"\"\"Dump knitting patterns to PNG files compatible with the AYAB software.\n\n\"\"\"\n\nfrom ..Dumper import ContentDumper\nfrom .Layout import GridLayout\nfrom .AYABPNGBuilder import AYABPNGBuilder\n\n\nclass AYABPNGDumper(ContentDumper):\n    \"\"\"This class converts knitting patterns into PNG files.\"\"\"\n\n    def __init__(self, function_that_returns_a_knitting_pattern_set):\n        \"\"\"Initialize the Dumper with a\n        :paramref:`function_that_returns_a_knitting_pattern_set`.\n\n        :param function_that_returns_a_knitting_pattern_set: a function that\n          takes no arguments but returns a\n          :class:`knittinpattern.KnittingPatternSet.KnittingPatternSet`\n\n        When a dump is requested, the\n        :paramref:`function_that_returns_a_knitting_pattern_set`\n        is called and the knitting pattern set is converted and saved to the\n        specified location.\n        \"\"\"\n        super().__init__(self._dump_knitting_pattern,\n                         text_is_expected=False, encoding=None)\n        self.__on_dump = function_that_returns_a_knitting_pattern_set\n\n    def _dump_knitting_pattern(self, file):\n        \"\"\"dump a knitting pattern to a file.\"\"\"\n        knitting_pattern_set = self.__on_dump()\n        knitting_pattern = knitting_pattern_set.patterns.at(0)\n        layout = GridLayout(knitting_pattern)\n        builder = AYABPNGBuilder(*layout.bounding_box)\n        builder.set_colors_in_grid(layout.walk_instructions())\n        builder.write_to_file(file)\n\n    def temporary_path(self, extension=\".png\"):\n        return super().temporary_path(extension=extension)\n    temporary_path.__doc__ = ContentDumper.temporary_path.__doc__\n\n\n__all__ = [\"AYABPNGDumper\"]\n"
  },
  {
    "path": "knittingpattern/convert/InstructionSVGCache.py",
    "content": "\"\"\"This module provides functionality to cache instruction SVGs.\"\"\"\nfrom .InstructionToSVG import default_instructions_to_svg\nfrom ..Dumper import SVGDumper\nfrom copy import deepcopy\nfrom collections import namedtuple\n\n_InstructionId = namedtuple(\"_InstructionId\", [\"type\", \"hex_color\"])\n\n\nclass InstructionSVGCache(object):\n\n    \"\"\"This class is a cache for SVG instructions.\n\n    If you plan too use only :meth:`instruction_to_svg_dict`, you are save to\n    replace a\n    :class:`knittingpsttern.convert.InstructionToSVG.InstructionToSVG` with\n    this cache to get faster results.\n    \"\"\"\n\n    def __init__(self, instruction_to_svg=None):\n        \"\"\"Create the InstructionSVGCache.\n\n        :param instruction_to_svg: an\n         :class:`~knittingpattern.convert.InstructionToSVG.InstructionToSVG`\n         object. If :obj:`None` is given, the\n         :func:`default_instructions_to_svg\n         <knittingpattern.convert.InstructionToSVG.default_instructions_to_svg>`\n         is used.\n        \"\"\"\n        if instruction_to_svg is None:\n            instruction_to_svg = default_instructions_to_svg()\n        self._instruction_to_svg_dict = \\\n            instruction_to_svg.instruction_to_svg_dict\n        self._cache = {}\n\n    def get_instruction_id(self, instruction_or_id):\n        \"\"\"The id that identifies the instruction in this cache.\n\n        :param instruction_or_id: an :class:`instruction\n          <knittingpattern.Instruction.Instruction>` or an instruction id\n        :return: a :func:`hashable <hash>` object\n        :rtype: tuple\n        \"\"\"\n        if isinstance(instruction_or_id, tuple):\n            return _InstructionId(instruction_or_id)\n        return _InstructionId(instruction_or_id.type,\n                              instruction_or_id.hex_color)\n\n    def _new_svg_dumper(self, on_dump):\n        \"\"\"Create a new SVGDumper with the function ``on_dump``.\n\n        :rtype: knittingpattern.Dumper.SVGDumper\n        \"\"\"\n        return SVGDumper(on_dump)\n\n    def to_svg(self, instruction_or_id,\n               i_promise_not_to_change_the_result=False):\n        \"\"\"Return the SVG for an instruction.\n\n        :param instruction_or_id: either an\n          :class:`~knittingpattern.Instruction.Instruction` or an id\n          returned by :meth:`get_instruction_id`\n        :param bool i_promise_not_to_change_the_result:\n\n          - :obj:`False`: the result is copied, you can alter it.\n          - :obj:`True`: the result is directly from the cache. If you change\n            the result, other calls of this function get the changed result.\n\n        :return: an SVGDumper\n        :rtype: knittingpattern.Dumper.SVGDumper\n        \"\"\"\n        return self._new_svg_dumper(lambda: self.instruction_to_svg_dict(\n            instruction_or_id, not i_promise_not_to_change_the_result))\n\n    def instruction_to_svg_dict(self, instruction_or_id, copy_result=True):\n        \"\"\"Return the SVG dict for the SVGBuilder.\n\n        :param instruction_or_id: the instruction or id, see\n          :meth:`get_instruction_id`\n        :param bool copy_result: whether to copy the result\n        :rtype: dict\n\n        The result is cached.\n        \"\"\"\n        instruction_id = self.get_instruction_id(instruction_or_id)\n        if instruction_id in self._cache:\n            result = self._cache[instruction_id]\n        else:\n            result = self._instruction_to_svg_dict(instruction_id)\n            self._cache[instruction_id] = result\n        if copy_result:\n            result = deepcopy(result)\n        return result\n\n\ndef default_instruction_svg_cache():\n    \"\"\"Return the default InstructionSVGCache.\n\n    :rtype: knittingpattern.convert.InstructionSVGCache.InstructionSVGCache\n    \"\"\"\n    global _default_instruction_svg_cache\n    if _default_instruction_svg_cache is None:\n        _default_instruction_svg_cache = InstructionSVGCache()\n    return _default_instruction_svg_cache\n_default_instruction_svg_cache = None\ndefault_svg_cache = default_instruction_svg_cache\n\n__all__ = [\"InstructionSVGCache\", \"default_instruction_svg_cache\",\n           \"default_svg_cache\"]\n"
  },
  {
    "path": "knittingpattern/convert/InstructionToSVG.py",
    "content": "\"\"\"This module maps instructions to SVG.\n\nUse :func:`default_instructions_to_svg` to load the svg files provided by\nthis package.\n\"\"\"\nimport os\nimport xmltodict\nfrom knittingpattern.Loader import PathLoader\n\n#:  The string to replace with the pattern name in the SVG file.\nREPLACE_IN_DEFAULT_SVG = \"{instruction.type}\"\n\n\nclass InstructionToSVG(object):\n    \"\"\"This class maps instructions to SVGs.\"\"\"\n\n    @property\n    def _loader_class(self):\n        \"\"\":return: the loader to load svgs from different locations\n        :rtype: knittingpattern.Loader.PathLoader .\"\"\"\n        return PathLoader\n\n    def __init__(self):\n        \"\"\"create a InstructionToSVG object without arguments.\"\"\"\n        self._instruction_type_to_file_content = {}\n\n    @property\n    def load(self):\n        \"\"\":return: a loader object that allows loading SVG files from\n          various sources such as files and folders.\n        :rtype: knittingpattern.Loader.PathLoader\n\n        Examples:\n\n        - ``instruction_to_svg.load.path(path)`` loads an SVG from a file named\n          path\n        - ``instruction_to_svg.load.folder(path)`` loads all SVG files for\n          instructions in the folder recursively.\n          If multiple files have the same name, the last occurrence is used.\n\n        \"\"\"\n        return self._loader_class(self._process_loaded_object)\n\n    def _process_loaded_object(self, path):\n        \"\"\"process the :paramref:`path`.\n\n        :param str path: the path to load an svg from\n        \"\"\"\n        file_name = os.path.basename(path)\n        name = os.path.splitext(file_name)[0]\n        with open(path) as file:\n            string = file.read()\n            self._instruction_type_to_file_content[name] = string\n\n    def instruction_to_svg_dict(self, instruction):\n        \"\"\"\n        :return: an xml-dictionary with the same content as\n          :meth:`instruction_to_svg`.\n        \"\"\"\n        instruction_type = instruction.type\n        if instruction_type in self._instruction_type_to_file_content:\n            svg = self._instruction_type_to_file_content[instruction_type]\n            return self._set_fills_in_color_layer(svg, instruction.hex_color)\n        return self.default_instruction_to_svg_dict(instruction)\n\n    def instruction_to_svg(self, instruction):\n        \"\"\":return: an SVG representing the instruction.\n\n        The SVG file is determined by the type attribute of the instruction.\n        An instruction of type ``\"knit\"`` is looked for in a file named\n        ``\"knit.svg\"``.\n\n        Every element inside a group labeled ``\"color\"`` of mode ``\"layer\"``\n        that has a ``\"fill\"`` style gets this fill replaced by the color of\n        the instruction.\n        Example of a recangle that gets filled like the instruction:\n\n        .. code:: xml\n\n            <g inkscape:label=\"color\" inkscape:groupmode=\"layer\">\n                <rect style=\"fill:#ff0000;fill-opacity:1;fill-rule:nonzero\"\n                      id=\"rectangle1\" width=\"10\" height=\"10\" x=\"0\" y=\"0\" />\n            </g>\n\n        If nothing was loaded to display this instruction, a default image is\n        be generated by :meth:`default_instruction_to_svg`.\n        \"\"\"\n        return xmltodict.unparse(self.instruction_to_svg_dict(instruction))\n\n    def _set_fills_in_color_layer(self, svg_string, color):\n        \"\"\"replaces fill colors in ``<g inkscape:label=\"color\"\n        inkscape:groupmode=\"layer\">`` with :paramref:`color`\n\n        :param color: a color fill the objects in the layer with\n        \"\"\"\n        structure = xmltodict.parse(svg_string)\n        if color is None:\n            return structure\n        layers = structure[\"svg\"][\"g\"]\n        if not isinstance(layers, list):\n            layers = [layers]\n        for layer in layers:\n            if not isinstance(layer, dict):\n                continue\n            if layer.get(\"@inkscape:label\") == \"color\" and \\\n                    layer.get(\"@inkscape:groupmode\") == \"layer\":\n                for key, elements in layer.items():\n                    if key.startswith(\"@\") or key.startswith(\"#\"):\n                        continue\n                    if not isinstance(elements, list):\n                        elements = [elements]\n                    for element in elements:\n                        style = element.get(\"@style\", None)\n                        if style:\n                            style = style.split(\";\")\n                            processed_style = []\n                            for style_element in style:\n                                if style_element.startswith(\"fill:\"):\n                                    style_element = \"fill:\" + color\n                                processed_style.append(style_element)\n                            style = \";\".join(processed_style)\n                            element[\"@style\"] = style\n        return structure\n\n    def has_svg_for_instruction(self, instruction):\n        \"\"\":return: whether there is an image for the instruction\n        :rtype: bool\n\n        This can be used before :meth:`instruction_to_svg` as it determines\n        whether\n\n        - the default value is used (:obj:`False`)\n        - or there is a dedicated svg representation (:obj:`True`).\n\n        \"\"\"\n        instruction_type = instruction.type\n        return instruction_type in self._instruction_type_to_file_content\n\n    def default_instruction_to_svg(self, instruction):\n        \"\"\"As :meth:`instruction_to_svg` but it only takes the ``default.svg``\n        file into account.\n\n        In case no file is found for an instruction in\n        :meth:`instruction_to_svg`,\n        this method is used to determine the default svg for it.\n\n        The content is created by replacing the text ``{instruction.type}`` in\n        the whole svg file named ``default.svg``.\n\n        If no file ``default.svg`` was loaded, an empty string is returned.\n        \"\"\"\n        svg_dict = self.default_instruction_to_svg_dict(instruction)\n        return xmltodict.unparse(svg_dict)\n\n    def default_instruction_to_svg_dict(self, instruction):\n        \"\"\"Returns an xml-dictionary with the same content as\n        :meth:`default_instruction_to_svg`\n\n        If no file ``default.svg`` was loaded, an empty svg-dict is returned.\n        \"\"\"\n        instruction_type = instruction.type\n        default_type = \"default\"\n        rep_str = \"{instruction.type}\"\n        if default_type not in self._instruction_type_to_file_content:\n            return {\"svg\": \"\"}\n        default_svg = self._instruction_type_to_file_content[default_type]\n        default_svg = default_svg.replace(rep_str, instruction_type)\n        colored_svg = self._set_fills_in_color_layer(default_svg,\n                                                     instruction.hex_color)\n        return colored_svg\n\n#: The name of the folder containing the svg files for the default\n#: instructions.\nDEFAULT_SVG_FOLDER = \"instruction-svgs\"\n\n\ndef default_instructions_to_svg():\n    \"\"\"load the default set of svg files for instructions\n\n    :return: the default svg files for the instructions in this package\n    :rtype: knittingpattern.InstructionToSVG.InstructionToSVG\n\n    \"\"\"\n    instruction_to_svg = InstructionToSVG()\n    instruction_to_svg.load.relative_folder(__name__, DEFAULT_SVG_FOLDER)\n    return instruction_to_svg\n\n__all__ = [\"InstructionToSVG\", \"default_instructions_to_svg\",\n           \"DEFAULT_SVG_FOLDER\"]\n"
  },
  {
    "path": "knittingpattern/convert/KnittingPatternToSVG.py",
    "content": "\"\"\"This module provides functionality to convert knitting patterns to SVG.\"\"\"\n\nfrom collections import OrderedDict\n\n#: Inside the svg, the instructions are put into definitions.\n#: The svg tag is renamed to the tag given in :data:`DEFINITION_HOLDER`.\nDEFINITION_HOLDER = \"g\"\n\n\nclass KnittingPatternToSVG(object):\n    \"\"\"Converts a KnittingPattern to SVG.\n\n    This is inspired by the method object pattern, since building an SVG\n    requires several steps.\n    \"\"\"\n\n    def __init__(self, knittingpattern, layout, instruction_to_svg, builder,\n                 zoom):\n        \"\"\"\n        :param knittingpattern.KnittingPattern.KnittingPattern knittingpattern:\n          a knitting pattern\n        :param knittingpattern.convert.Layout.GridLayout layout:\n        :param instruction_to_svg: an\n          :class:`~knittingpattern.convert.InstructionToSVG.InstructionToSVG`\n          :class:`\n          ~knittingpattern.convert.InstructionToSVGCache.InstructionSVGCache`,\n          both with instructions already loaded.\n        :param knittingpattern.convert.SVGBuilder.SVGBuilder builder:\n        :param float zoom: the height and width of a knit instruction\n        \"\"\"\n        self._knittingpattern = knittingpattern\n        self._layout = layout\n        self._instruction_to_svg = instruction_to_svg\n        self._builder = builder\n        self._zoom = zoom\n        self._instruction_type_color_to_symbol = OrderedDict()\n        self._symbol_id_to_scale = {}\n\n    def build_SVG_dict(self):\n        \"\"\"Go through the layout and build the SVG.\n\n        :return: an xml dict that can be exported using a\n          :class:`~knittingpattern.Dumper.XMLDumper`\n        :rtype: dict\n        \"\"\"\n        zoom = self._zoom\n        layout = self._layout\n        builder = self._builder\n        bbox = list(map(lambda f: f * zoom, layout.bounding_box))\n        builder.bounding_box = bbox\n        flip_x = bbox[2] + bbox[0] * 2\n        flip_y = bbox[3] + bbox[1] * 2\n        instructions = list(layout.walk_instructions(\n            lambda i: (flip_x - (i.x + i.width) * zoom,\n                       flip_y - (i.y + i.height) * zoom,\n                       i.instruction)))\n        instructions.sort(key=lambda x_y_i: x_y_i[2].render_z)\n        for x, y, instruction in instructions:\n            render_z = instruction.render_z\n            z_id = (\"\" if not render_z else \"-{}\".format(render_z))\n            layer_id = \"row-{}{}\".format(instruction.row.id, z_id)\n            def_id = self._register_instruction_in_defs(instruction)\n            scale = self._symbol_id_to_scale[def_id]\n            group = {\n                \"@class\": \"instruction\",\n                \"@id\": \"instruction-{}\".format(instruction.id),\n                \"@transform\": \"translate({},{}),scale({})\".format(\n                    x, y, scale)\n            }\n            builder.place_svg_use(def_id, layer_id, group)\n        builder.insert_defs(self._instruction_type_color_to_symbol.values())\n        return builder.get_svg_dict()\n\n    def _register_instruction_in_defs(self, instruction):\n        \"\"\"Create a definition for the instruction.\n\n        :return: the id of a symbol in the defs for the specified\n          :paramref:`instruction`\n        :rtype: str\n\n        If no symbol yet exists in the defs for the :paramref:`instruction` a\n        symbol is created and saved using :meth:`_make_symbol`.\n        \"\"\"\n        type_ = instruction.type\n        color_ = instruction.color\n        instruction_to_svg_dict = \\\n            self._instruction_to_svg.instruction_to_svg_dict\n        instruction_id = \"{}:{}\".format(type_, color_)\n        defs_id = instruction_id + \":defs\"\n        if instruction_id not in self._instruction_type_color_to_symbol:\n            svg_dict = instruction_to_svg_dict(instruction)\n            self._compute_scale(instruction_id, svg_dict)\n            symbol = self._make_definition(svg_dict, instruction_id)\n            self._instruction_type_color_to_symbol[defs_id] = \\\n                symbol[DEFINITION_HOLDER].pop(\"defs\", {})\n            self._instruction_type_color_to_symbol[instruction_id] = symbol\n        return instruction_id\n\n    def _make_definition(self, svg_dict, instruction_id):\n        \"\"\"Create a symbol out of the supplied :paramref:`svg_dict`.\n\n        :param dict svg_dict: dictionary containing the SVG for the\n          instruction currently processed\n        :param str instruction_id: id that will be assigned to the symbol\n        \"\"\"\n        instruction_def = svg_dict[\"svg\"]\n        blacklisted_elements = [\"sodipodi:namedview\", \"metadata\"]\n        whitelisted_attributes = [\"@sodipodi:docname\"]\n        symbol = OrderedDict({\"@id\": instruction_id})\n        for content, value in instruction_def.items():\n            if content.startswith('@'):\n                if content in whitelisted_attributes:\n                    symbol[content] = value\n            elif content not in blacklisted_elements:\n                symbol[content] = value\n        return {DEFINITION_HOLDER: symbol}\n\n    def _compute_scale(self, instruction_id, svg_dict):\n        \"\"\"Compute the scale of an instruction svg.\n\n        Compute the scale using the bounding box stored in the\n        :paramref:`svg_dict`. The scale is saved in a dictionary using\n        :paramref:`instruction_id` as key.\n\n        :param str instruction_id: id identifying a symbol in the defs\n        :param dict svg_dict: dictionary containing the SVG for the\n          instruction currently processed\n        \"\"\"\n        bbox = list(map(float, svg_dict[\"svg\"][\"@viewBox\"].split()))\n        scale = self._zoom / (bbox[3] - bbox[1])\n        self._symbol_id_to_scale[instruction_id] = scale\n\n__all__ = [\"KnittingPatternToSVG\", \"DEFINITION_HOLDER\"]\n"
  },
  {
    "path": "knittingpattern/convert/Layout.py",
    "content": "\"\"\"Map ``(x, y)`` coordinates to instructions\n\n\"\"\"\nfrom itertools import chain\nfrom collections import namedtuple\n\n\nINSTRUCTION_HEIGHT = 1  #: the default height of an instruction in the grid\n\n#: This is the key to the \"grid-layout\"\n#:\n#: Values for the layout can be specified in each instruction.\n#:\n#: \"grid-layout\" : {\n#:     \"width\" : 1\n#: }\n#:\n#: .. seealso:: :data:`width`\nGRID_LAYOUT = \"grid-layout\"\n\n#: the width of the instruction in the grid layout, if specified.\n#: .. seealso:: :data:`GRID_LAYOUT`\nWIDTH = \"width\"\nPoint = namedtuple(\"Point\", [\"x\", \"y\"])\n\n\nclass InGrid(object):\n\n    \"\"\"Base class for things in a grid\"\"\"\n\n    def __init__(self, position):\n        \"\"\"Create a new InGrid object.\"\"\"\n        self._position = position\n\n    @property\n    def x(self):\n        \"\"\":return: x coordinate in the grid\n        :rtype: float\n        \"\"\"\n        return self._position.x\n\n    @property\n    def y(self):\n        \"\"\":return: y coordinate in the grid\n        :rtype: float\n        \"\"\"\n        return self._position.y\n\n    @property\n    def xy(self):\n        \"\"\":return: ``(x, y)`` coordinate in the grid\n        :rtype: tuple\n        \"\"\"\n        return self._position\n\n    @property\n    def yx(self):\n        \"\"\":return: ``(y, x)`` coordinate in the grid\n        :rtype: tuple\n        \"\"\"\n        return self._position.y, self._position.x\n\n    @property\n    def width(self):\n        \"\"\":return: width of the object on the grid\n        :rtype: float\n        \"\"\"\n\n        return self._width\n\n    @property\n    def height(self):\n        \"\"\":return: height of the object on the grid\n        :rtype: float\n        \"\"\"\n        return INSTRUCTION_HEIGHT\n\n    @property\n    def row(self):\n        \"\"\":return: row of the object on the grid\n        :rtype: knittingpattern.Row.Row\n        \"\"\"\n        return self._row\n\n    @property\n    def bounding_box(self):\n        \"\"\"The bounding box of this object.\n\n        :return: (min x, min y, max x, max y)\n        :rtype: tuple\n        \"\"\"\n        return self._bounding_box\n\n    @property\n    def id(self):\n        \"\"\"The id of this object.\"\"\"\n        return self._id\n\n\nclass InstructionInGrid(InGrid):\n\n    \"\"\"Holder of an instruction in the GridLayout.\"\"\"\n\n    def __init__(self, instruction, position):\n        \"\"\"\n        :param instruction: an :class:`instruction\n          <knittingpattern.Instruction.InstructionInRow>`\n        :param Point position: the position of the :paramref:`instruction`\n\n        \"\"\"\n        self._instruction = instruction\n        super().__init__(position)\n\n    @property\n    def _width(self):\n        \"\"\"For ``self.width``.\"\"\"\n        layout = self._instruction.get(GRID_LAYOUT)\n        if layout is not None:\n            width = layout.get(WIDTH)\n            if width is not None:\n                return width\n        return self._instruction.number_of_consumed_meshes\n\n    @property\n    def instruction(self):\n        \"\"\"The instruction.\n\n        :return: instruction that is placed on the grid\n        :rtype: knittingpattern.Instruction.InstructionInRow\n        \"\"\"\n        return self._instruction\n\n    @property\n    def color(self):\n        \"\"\"The color of the instruction.\n\n        :return: the color of the :attr:`instruction`\n        \"\"\"\n        return self._instruction.color\n\n    def _row(self):\n        \"\"\"For ``self.row``.\"\"\"\n        return self._instruction.row\n\n\nclass RowInGrid(InGrid):\n    \"\"\"Assign x and y coordinates to rows.\"\"\"\n\n    def __init__(self, row, position):\n        \"\"\"Create a new row in the grid.\"\"\"\n        super().__init__(position)\n        self._row = row\n\n    @property\n    def _width(self):\n        \"\"\":return: the number of consumed meshes\"\"\"\n        return sum(map(lambda i: i.width, self.instructions))\n\n    @property\n    def instructions(self):\n        \"\"\"The instructions in a grid.\n\n        :return: the :class:`instructions in a grid <InstructionInGrid>` of\n          this row\n        :rtype: list\n        \"\"\"\n        x = self.x\n        y = self.y\n        result = []\n        for instruction in self._row.instructions:\n            instruction_in_grid = InstructionInGrid(instruction, Point(x, y))\n            x += instruction_in_grid.width\n            result.append(instruction_in_grid)\n        return result\n\n    @property\n    def _bounding_box(self):\n        min_x = self.x\n        min_y = self.y\n        max_x = min_x + max(self._row.number_of_consumed_meshes,\n                            self._row.number_of_produced_meshes)\n        max_y = min_y + self.height\n        return min_x, min_y, max_x, max_y\n\n    @property\n    def _id(self):\n        return self._row.id\n\n\ndef identity(object_):\n    \"\"\":return: the argument\"\"\"\n    return object_\n\n\nclass _RecursiveWalk(object):\n    \"\"\"This class starts walking the knitting pattern and maps instructions to\n    positions in the grid that is created.\"\"\"\n\n    def __init__(self, first_instruction):\n        \"\"\"Start walking the knitting pattern starting from first_instruction.\n        \"\"\"\n        self._rows_in_grid = {}\n        self._todo = []\n        self._expand(first_instruction.row, Point(0, 0), [])\n        self._walk()\n\n    def _expand(self, row, consumed_position, passed):\n        \"\"\"Add the arguments `(args, kw)` to `_walk` to the todo list.\"\"\"\n        self._todo.append((row, consumed_position, passed))\n\n    def _step(self, row, position, passed):\n        \"\"\"Walk through the knitting pattern by expanding an row.\"\"\"\n        if row in passed or not self._row_should_be_placed(row, position):\n            return\n        self._place_row(row, position)\n        passed = [row] + passed\n        # print(\"{}{} at\\t{} {}\".format(\"  \" * len(passed), row, position,\n        #                               passed))\n        for i, produced_mesh in enumerate(row.produced_meshes):\n            self._expand_produced_mesh(produced_mesh, i, position, passed)\n        for i, consumed_mesh in enumerate(row.consumed_meshes):\n            self._expand_consumed_mesh(consumed_mesh, i, position, passed)\n\n    def _expand_consumed_mesh(self, mesh, mesh_index, row_position, passed):\n        \"\"\"expand the consumed meshes\"\"\"\n        if not mesh.is_produced():\n            return\n        row = mesh.producing_row\n        position = Point(\n            row_position.x + mesh.index_in_producing_row - mesh_index,\n            row_position.y - INSTRUCTION_HEIGHT\n        )\n        self._expand(row, position, passed)\n\n    def _expand_produced_mesh(self, mesh, mesh_index, row_position, passed):\n        \"\"\"expand the produced meshes\"\"\"\n        if not mesh.is_consumed():\n            return\n        row = mesh.consuming_row\n        position = Point(\n            row_position.x - mesh.index_in_consuming_row + mesh_index,\n            row_position.y + INSTRUCTION_HEIGHT\n        )\n        self._expand(row, position, passed)\n\n    def _row_should_be_placed(self, row, position):\n        \"\"\":return: whether to place this instruction\"\"\"\n        placed_row = self._rows_in_grid.get(row)\n        return placed_row is None or placed_row.y < position.y\n\n    def _place_row(self, row, position):\n        \"\"\"place the instruction on a grid\"\"\"\n        self._rows_in_grid[row] = RowInGrid(row, position)\n\n    def _walk(self):\n        \"\"\"Loop through all the instructions that are `_todo`.\"\"\"\n        while self._todo:\n            args = self._todo.pop(0)\n            self._step(*args)\n\n    def instruction_in_grid(self, instruction):\n        \"\"\"Returns an `InstructionInGrid` object for the `instruction`\"\"\"\n        row_position = self._rows_in_grid[instruction.row].xy\n        x = instruction.index_of_first_consumed_mesh_in_row\n        position = Point(row_position.x + x, row_position.y)\n        return InstructionInGrid(instruction, position)\n\n    def row_in_grid(self, row):\n        \"\"\"Returns an `RowInGrid` object for the `row`\"\"\"\n        return self._rows_in_grid[row]\n\n\nclass Connection(object):\n    \"\"\"a connection between two :class:`InstructionInGrid` objects\"\"\"\n\n    def __init__(self, start, stop):\n        \"\"\"\n        :param InstructionInGrid start: the start of the connection\n        :param InstructionInGrid stop: the end of the connection\n        \"\"\"\n        self._start = start\n        self._stop = stop\n\n    @property\n    def start(self):\n        \"\"\":return: the start of the connection\n        :rtype: InstructionInGrid\n        \"\"\"\n        return self._start\n\n    @property\n    def stop(self):\n        \"\"\":return: the end of the connection\n        :rtype: InstructionInGrid\n        \"\"\"\n        return self._stop\n\n    def is_visible(self):\n        \"\"\":return: is this connection is visible\n        :rtype: bool\n\n        A connection is visible if it is longer that 0.\"\"\"\n        if self._start.y + 1 < self._stop.y:\n            return True\n        return False\n\n\nclass GridLayout(object):\n    \"\"\"This class places the instructions at ``(x, y)`` positions.\"\"\"\n\n    def __init__(self, pattern):\n        \"\"\"\n        :param knittingpattern.KnittingPattern.KnittingPattern pattern: the\n          pattern to layout\n\n        \"\"\"\n        self._pattern = pattern\n        self._rows = list(pattern.rows)\n        self._walk = _RecursiveWalk(self._rows[0].instructions[0])\n        self._rows.sort(key=lambda row: self._walk.row_in_grid(row).yx)\n\n    def walk_instructions(self, mapping=identity):\n        \"\"\"Iterate over instructions.\n\n        :return: an iterator over :class:`instructions in grid\n          <InstructionInGrid>`\n        :param mapping: funcion to map the result\n\n        .. code:: python\n\n            for pos, c in layout.walk_instructions(lambda i: (i.xy, i.color)):\n                print(\"color {} at {}\".format(c, pos))\n\n        \"\"\"\n        instructions = chain(*self.walk_rows(lambda row: row.instructions))\n        return map(mapping, instructions)\n\n    def walk_rows(self, mapping=identity):\n        \"\"\"Iterate over rows.\n\n        :return: an iterator over :class:`rows <RowsInGrid>`\n        :param mapping: funcion to map the result, see\n          :meth:`walk_instructions` for an example usage\n        \"\"\"\n        row_in_grid = self._walk.row_in_grid\n        return map(lambda row: mapping(row_in_grid(row)), self._rows)\n\n    def walk_connections(self, mapping=identity):\n        \"\"\"Iterate over connections between instructions.\n\n        :return: an iterator over :class:`connections <Connection>` between\n          :class:`instructions in grid <InstructionInGrid>`\n        :param mapping: funcion to map the result, see\n          :meth:`walk_instructions` for an example usage\n        \"\"\"\n        for start in self.walk_instructions():\n            for stop_instruction in start.instruction.consuming_instructions:\n                if stop_instruction is None:\n                    continue\n                stop = self._walk.instruction_in_grid(stop_instruction)\n                connection = Connection(start, stop)\n                if connection.is_visible():\n                    # print(\"connection:\",\n                    #      connection.start.instruction,\n                    #      connection.stop.instruction)\n                    yield mapping(connection)\n\n    @property\n    def bounding_box(self):\n        \"\"\"The minimum and maximum bounds of this layout.\n\n        :return: ``(min_x, min_y, max_x, max_y)`` the bounding box\n          of this layout\n        :rtype: tuple\n        \"\"\"\n        min_x, min_y, max_x, max_y = zip(*list(self.walk_rows(\n            lambda row: row.bounding_box)))\n        return min(min_x), min(min_y), max(max_x), max(max_y)\n\n    def row_in_grid(self, row):\n        \"\"\"The a RowInGrid for the row with position information.\n\n        :return: a row in the grid\n        :rtype: RowInGrid\n        \"\"\"\n        return self._walk.row_in_grid(row)\n\n\n__all__ = [\"GridLayout\", \"InstructionInGrid\", \"Connection\", \"identity\",\n           \"Point\", \"INSTRUCTION_HEIGHT\", \"InGrid\", \"RowInGrid\"]\n"
  },
  {
    "path": "knittingpattern/convert/SVGBuilder.py",
    "content": "\"\"\"build SVG files\n\n\n\"\"\"\nimport xmltodict\n\n#: an empty svg file as a basis\nSVG_FILE = \"\"\"\n<svg\n   xmlns:ns=\"http://PURL.org/dc/elements/1.1/\"\n   xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n   xmlns:cc=\"http://creativecommons.org/ns#\"\n   xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n   xmlns:svg=\"http://www.w3.org/2000/svg\"\n   xmlns=\"http://www.w3.org/2000/svg\"\n   xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n   xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"\n   xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n    <title>knittingpattern</title>\n    <defs></defs>\n</svg>\n\"\"\"\n\n\nclass SVGBuilder(object):\n    \"\"\"This class builds an SVG to a file.\n\n    The class itself does not know what the objects look like.\n    It offers a more convinient interface to build SVG files.\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize this object without arguments.\"\"\"\n        self._structure = xmltodict.parse(SVG_FILE)\n        self._layer_id_to_layer = {}\n        self._svg = self._structure[\"svg\"]\n        self._min_x = None\n        self._min_y = None\n        self._max_x = None\n        self._max_y = None\n\n    @property\n    def bounding_box(self):\n        \"\"\"the bounding box of this SVG\n        ``(min_x, min_y, max_x, max_y)``.\n\n        .. code:: python\n\n            svg_builder10x10.bounding_box = (0, 0, 10, 10)\n            assert svg_builder10x10.bounding_box == (0, 0, 10, 10)\n\n        ``viewBox``, ``width`` and ``height`` are computed from this.\n\n        If the bounding box was never set, the result is a tuple of four\n        :obj:`None`.\n        \"\"\"\n        return (self._min_x, self._min_y, self._max_x, self._max_y)\n\n    @bounding_box.setter\n    def bounding_box(self, bbox):\n        min_x, min_y, max_x, max_y = bbox\n        self._min_x = min_x\n        self._min_y = min_y\n        self._max_x = max_x\n        self._max_y = max_y\n        self._svg[\"@height\"] = str(max_y - min_y)\n        self._svg[\"@width\"] = str(max_x - min_x)\n        self._svg[\"@viewBox\"] = \"{} {} {} {}\".format(min_x, min_y,\n                                                     max_x, max_y)\n\n    def place(self, x, y, svg, layer_id):\n        \"\"\"Place the :paramref:`svg` content at ``(x, y)`` position\n        in the SVG, in a layer with the id :paramref:`layer_id`.\n\n        :param float x: the x position of the svg\n        :param float y: the y position of the svg\n        :param str svg: the SVG to place at ``(x, y)``\n        :param str layer_id: the id of the layer that this\n          :paramref:`svg` should be placed inside\n\n        \"\"\"\n        content = xmltodict.parse(svg)\n        self.place_svg_dict(x, y, content, layer_id)\n\n    def place_svg_dict(self, x, y, svg_dict, layer_id, group=None):\n        \"\"\"Same as :meth:`place` but with a dictionary as :paramref:`svg_dict`.\n\n        :param dict svg_dict: a dictionary returned by `xmltodict.parse()\n          <https://github.com/martinblech/xmltodict>`__\n        :param dict group: a dictionary of values to add to the group the\n          :paramref:`svg_dict` will be added to or :obj:`None` if nothing\n          should be added\n        \"\"\"\n        if group is None:\n            group = {}\n        group_ = {\n            \"@transform\": \"translate({},{})\".format(x, y),\n            \"g\": list(svg_dict.values())\n        }\n        group_.update(group)\n        layer = self._get_layer(layer_id)\n        layer[\"g\"].append(group_)\n\n    def place_svg_use_coords(self, x, y, symbol_id, layer_id, group=None):\n        \"\"\"Similar to :meth:`place` but with an id as :paramref:`symbol_id`.\n\n        :param str symbol_id: an id which identifies an svg object defined in\n          the defs\n        :param dict group: a dictionary of values to add to the group the\n          use statement will be added to or :obj:`None` if nothing\n          should be added\n        \"\"\"\n        if group is None:\n            group = {}\n        use = {\"@x\": x, \"@y\": y, \"@xlink:href\": \"#{}\".format(symbol_id)}\n        group_ = {\"use\": use}\n        group_.update(group)\n        layer = self._get_layer(layer_id)\n        layer[\"g\"].append(group_)\n\n    def place_svg_use(self, symbol_id, layer_id, group=None):\n        \"\"\"Same as :meth:`place_svg_use_coords`.\n\n        With implicit `x`  and `y` which are set to `0` in this method and then\n        :meth:`place_svg_use_coords` is called.\n        \"\"\"\n        self.place_svg_use_coords(0, 0, symbol_id, layer_id, group)\n\n    def _get_layer(self, layer_id):\n        \"\"\"\n        :return: the layer with the :paramref:`layer_id`. If the layer\n          does not exist, it is created.\n        :param str layer_id: the id of the layer\n        \"\"\"\n        if layer_id not in self._layer_id_to_layer:\n            self._svg.setdefault(\"g\", [])\n            layer = {\n                \"g\": [],\n                \"@inkscape:label\": layer_id,\n                \"@id\": layer_id,\n                \"@inkscape:groupmode\": \"layer\",\n                \"@class\": \"row\"\n            }\n            self._layer_id_to_layer[layer_id] = layer\n            self._svg[\"g\"].append(layer)\n        return self._layer_id_to_layer[layer_id]\n\n    def insert_defs(self, defs):\n        \"\"\"Adds the defs to the SVG structure.\n\n        :param defs: a list of SVG dictionaries, which contain the defs,\n          which should be added to the SVG structure.\n        \"\"\"\n        if self._svg[\"defs\"] is None:\n            self._svg[\"defs\"] = {}\n        for def_ in defs:\n            for key, value in def_.items():\n                if key.startswith(\"@\"):\n                    continue\n                if key not in self._svg[\"defs\"]:\n                    self._svg[\"defs\"][key] = []\n                if not isinstance(value, list):\n                    value = [value]\n                self._svg[\"defs\"][key].extend(value)\n\n    def get_svg_dict(self):\n        \"\"\"Return the SVG structure generated.\"\"\"\n        return self._structure\n\n    def write_to_file(self, file):\n        \"\"\"Writes the current SVG to the :paramref:`file`.\n\n        :param file: a file-like object\n        \"\"\"\n        xmltodict.unparse(self._structure, file, pretty=True)\n\n\n__all__ = [\"SVGBuilder\", \"SVG_FILE\"]\n"
  },
  {
    "path": "knittingpattern/convert/__init__.py",
    "content": "\"\"\"Convert knitting patterns.\n\nUsually you do not need to import this. Convenience functions should be\navailable in the :mod:`knittingpattern` module.\n\"\"\"\n"
  },
  {
    "path": "knittingpattern/convert/color.py",
    "content": "\"\"\"Functions for color conversion.\"\"\"\nimport webcolors\n\n\ndef convert_color_to_rrggbb(color):\n    \"\"\"The color in \"#RRGGBB\" format.\n\n    :return: the :attr:`color` in \"#RRGGBB\" format\n    \"\"\"\n    if not color.startswith(\"#\"):\n        rgb = webcolors.html5_parse_legacy_color(color)\n        hex_color = webcolors.html5_serialize_simple_color(rgb)\n    else:\n        hex_color = color\n    return webcolors.normalize_hex(hex_color)\n\n__all__ = [\"convert_color_to_rrggbb\"]\n"
  },
  {
    "path": "knittingpattern/convert/image_to_knittingpattern.py",
    "content": "\"\"\"This file lets you convert image files to knitting patterns.\n\n\"\"\"\nimport PIL.Image\nfrom ..Loader import PathLoader\nfrom ..Dumper import JSONDumper\nfrom .load_and_dump import decorate_load_and_dump\nimport os\n\n\n@decorate_load_and_dump(PathLoader, JSONDumper)\ndef convert_image_to_knitting_pattern(path, colors=(\"white\", \"black\")):\n    \"\"\"Load a image file such as a png bitmap of jpeg file and convert it\n    to a :ref:`knitting pattern file <FileFormatSpecification>`.\n\n    :param list colors: a list of strings that should be used as\n      :ref:`colors <png-color>`.\n    :param str path: ignore this. It is fulfilled by the loeder.\n\n    Example:\n\n    .. code:: python\n\n        convert_image_to_knitting_pattern().path(\"image.png\").path(\"image.json\")\n    \"\"\"\n    image = PIL.Image.open(path)\n    pattern_id = os.path.splitext(os.path.basename(path))[0]\n    rows = []\n    connections = []\n    pattern_set = {\n        \"version\": \"0.1\",\n        \"type\": \"knitting pattern\",\n        \"comment\": {\n            \"source\": path\n        },\n        \"patterns\": [\n            {\n                \"name\": pattern_id,\n                \"id\": pattern_id,\n                \"rows\": rows,\n                \"connections\": connections\n            }\n        ]}\n    bbox = image.getbbox()\n    if not bbox:\n        return pattern_set\n    white = image.getpixel((0, 0))\n    min_x, min_y, max_x, max_y = bbox\n    last_row_y = None\n    for y in reversed(range(min_y, max_y)):\n        instructions = []\n        row = {\"id\": y, \"instructions\": instructions}\n        rows.append(row)\n        for x in range(min_x, max_x):\n            if image.getpixel((x, y)) == white:\n                color = colors[0]\n            else:\n                color = colors[1]\n            instruction = {\"color\": color}\n            instructions.append(instruction)\n        if last_row_y is not None:\n            connections.append({\"from\": {\"id\": last_row_y}, \"to\": {\"id\": y}})\n        last_row_y = y\n    return pattern_set\n\n\n__all__ = [\"convert_image_to_knitting_pattern\"]\n"
  },
  {
    "path": "knittingpattern/convert/load_and_dump.py",
    "content": "\"\"\"convinience methods for conversion\n\nBest to use :meth:`decorate_load_and_dump`.\n\n\"\"\"\nfrom functools import wraps\n\n\ndef load_and_dump(create_loader, create_dumper, load_and_dump_):\n    \"\"\":return: a function that has the doc string of\n      :paramref:`load_and_dump_`\n      additional arguments to this function are passed on to\n      :paramref:`load_and_dump_`.\n\n    :param create_loader: a loader, e.g.\n      :class:`knittingpattern.Loader.PathLoader`\n    :param create_dumper: a dumper, e.g.\n      :class:`knittingpattern.Dumper.ContentDumper`\n    :param load_and_dump_: a function to call with the loaded content.\n      The arguments to both, :paramref:`create_dumper` and,\n      :paramref:`create_loader`\n      will be passed to :paramref:`load_and_dump_`.\n      Any additional arguments to the return value are also passed to\n      :paramref:`load_and_dump_`.\n      The return value of :paramref:`load_and_dump_` is passed back to the\n      :paramref:`Dumper`.\n\n    .. seealso:: :func:`decorate_load_and_dump`\n    \"\"\"\n    @wraps(load_and_dump_)\n    def load_and_dump__(*args1, **kw):\n        \"\"\"Return the loader.\"\"\"\n        def load(*args2):\n            \"\"\"Return the dumper.\"\"\"\n            def dump(*args3):\n                \"\"\"Dump the object.\"\"\"\n                return load_and_dump_(*(args2 + args3 + args1), **kw)\n            return create_dumper(dump)\n        return create_loader(load)\n    return load_and_dump__\n\n\ndef decorate_load_and_dump(create_loader, create_dumper):\n    \"\"\"Same as :func:`load_and_dump` but returns a function to enable decorator\n    syntax.\n\n    Examples:\n\n    .. code:: Python\n\n        @decorate_load_and_dump(ContentLoader, JSONDumper)\n        def convert_from_loader_to_dumper(loaded_stuff, other=\"arguments\"):\n            # convert\n            return converted_stuff\n\n        @decorate_load_and_dump(PathLoader, lambda dump: ContentDumper(dump,\n            encoding=None))\n        def convert_from_loader_to_dumper(loaded_stuff, to_file):\n            # convert\n            to_file.write(converted_stuff)\n\n    \"\"\"\n    return lambda func: load_and_dump(create_loader, create_dumper, func)\n\n\n__all__ = [\"load_and_dump\", \"decorate_load_and_dump\"]\n"
  },
  {
    "path": "knittingpattern/convert/test/test_AYABPNGBuilder.py",
    "content": "\"\"\"Test creating png files from knitting patterns.\n\nEach pixel is an instruction.\"\"\"\nfrom test_convert import fixture, pytest, MagicMock, call\nfrom knittingpattern.convert.AYABPNGBuilder import AYABPNGBuilder\nfrom collections import namedtuple\nimport PIL.Image\nimport tempfile\n\n\nColorInGrid = namedtuple(\"ColorInGrid\", [\"x\", \"y\", \"color\"])\n\n\n@fixture\ndef builder():\n    return AYABPNGBuilder(-1, -1, 10, 5)\n\n\nclass TestColorConversion(object):\n    \"\"\"Convert color names to RGB colors.\n\n    One could use the webcolors package for that:\n      https://pypi.python.org/pypi/webcolors/\n    \"\"\"\n\n    @fixture\n    def convert(self):\n        return AYABPNGBuilder._convert_color_to_rrggbb\n\n    def test_convert_24_bit(self, convert):\n        assert convert(\"#123456\") == \"#123456\"\n\n    def test_convert_blue(self, convert):\n        assert convert(\"blue\") == \"#0000ff\"\n\n    def test_can_convert_anything_to_color(self, convert):\n        assert convert(\"ajsdkahsj\") != convert(\"ajsahsj\")\n\n\nclass TestBounds(object):\n    \"\"\"Check whether points are inside and outside of the bounds.\"\"\"\n    @pytest.mark.parametrize('x, y', [(0, 0), (-1, 0), (0, -1), (0, 4),\n                                      (9, 4)])\n    def test_inside(self, builder, x, y):\n        assert builder.is_in_bounds(x, y)\n\n    @pytest.mark.parametrize('x, y', [(-2, -2), (10, 0), (5, 5), (30, 30),\n                                      (12, 12)])\n    def test_outside(self, builder, x, y):\n        assert not builder.is_in_bounds(x, y)\n\n\nclass TestSetPixel(object):\n\n    @fixture\n    def set(self):\n        return MagicMock()\n\n    @fixture\n    def patched(self, builder, set):\n        builder._set_pixel = set\n        return builder\n\n    def test_set_pixel(self, patched, set):\n        patched.set_pixel(1, 2, \"#aaaaaa\")\n        set.assert_called_with(1, 2, \"#aaaaaa\")\n\n    def test_set_pixel_converts_color(self, patched, set):\n        patched.set_pixel(2, 3, \"black\")\n        set.assert_called_with(2, 3, \"#000000\")\n\n    def test_set_with_instruction(self, patched, set):\n        patched.set_color_in_grid(ColorInGrid(0, 0, \"#adadad\"))\n        set.assert_called_with(0, 0, \"#adadad\")\n\n    def test_call_many_instructions(self, patched, set):\n        patched.set_colors_in_grid([\n            ColorInGrid(0, 0, \"#000000\"),\n            ColorInGrid(0, 1, \"#111111\"),\n            ColorInGrid(2, 0, \"#222222\")\n        ])\n        set.assert_has_calls([call(0, 0, \"#000000\"),\n                              call(0, 1, \"#111111\"),\n                              call(2, 0, \"#222222\")])\n\n    def test_setiing_color_none_does_nothing(self, patched, set):\n        patched.set_pixel(2, 2, None)\n        patched.set_color_in_grid(ColorInGrid(0, 0, None))\n        patched.set_colors_in_grid([\n            ColorInGrid(0, 0, None),\n            ColorInGrid(0, 1, None),\n            ColorInGrid(2, 0, None)\n        ])\n        assert not set.called\n\n\nclass TestSavingAsPNG(object):\n\n    @fixture(scope=\"class\")\n    def image_path(self):\n        return tempfile.mktemp()\n\n    @fixture(scope=\"class\")\n    def builder(self, image_path):\n        builder = AYABPNGBuilder(-1, -1, 2, 2)\n        # set pixels inside\n        builder.set_pixel(0, 0, \"#000000\")\n        builder.set_pixel(-1, -1, \"#111111\")\n        builder.set_pixel(1, 1, \"#222222\")\n        # set out of bounds pixels\n        builder.set_colors_in_grid([ColorInGrid(12, 12, \"red\")])\n        builder.set_color_in_grid(ColorInGrid(-3, -3, \"#adadad\"))\n        builder.set_pixel(-3, 4, \"#adadad\")\n        builder.write_to_file(image_path)\n        return builder\n\n    @fixture(scope=\"class\")\n    def image(self, image_path, builder):\n        return PIL.Image.open(image_path)\n\n    def test_pixels_are_set(self, image):\n        assert image.getpixel((1, 1)) == (0, 0, 0)\n        assert image.getpixel((0, 0)) == (0x11, 0x11, 0x11)\n        assert image.getpixel((2, 2)) == (0x22, 0x22, 0x22)\n\n    def test_bbox_is_3x3(self, image):\n        assert image.getbbox() == (0, 0, 3, 3)\n\n    def test_other_pixels_have_default_color(self, image):\n        assert image.getpixel((1, 2)) == (255, 255, 255)\n\n\nclass TestDefaultColor(object):\n\n    @fixture\n    def default_color(self, builder):\n        return builder.default_color\n\n    def test_can_change_default_color(self):\n        builder = AYABPNGBuilder(-1, -1, 2, 2, \"black\")\n        assert builder.default_color == \"black\"\n\n    def test_default_color_is_white(self, default_color):\n        assert default_color == \"white\"\n"
  },
  {
    "path": "knittingpattern/convert/test/test_SVGBuilder.py",
    "content": "from test_convert import fixture, parse_file, raises\nfrom knittingpattern.convert.SVGBuilder import SVGBuilder\nimport io\n\nBBOX = (-1, -2, 5, 10)\n\n\n@fixture\ndef file():\n    return io.StringIO()\n\n\n@fixture\ndef builder():\n    builder = SVGBuilder()\n    builder.bounding_box = BBOX\n    return builder\n\n\n@fixture\ndef svg(builder, file):\n    def svg():\n        builder.write_to_file(file)\n        file.seek(0)\n        print(file.read())\n        file.seek(0)\n        return parse_file(file).svg\n    return svg\n\n\n@fixture\ndef svg1(builder, svg):\n    instruction = \"<instruction id=\\\"inst{}-id\\\"></instruction>\"\n    builder.place(0, 0, instruction.format(1), \"row1\")\n    builder.place(1, 0, instruction.format(2), \"row1\")\n    builder.place(2, 0, instruction.format(3), \"row1\")\n    builder.place(0, 1, instruction.format(4), \"row2\")\n    builder.place(1, 1, instruction.format(5), \"row2\")\n    builder.place(2.0, 1.0, instruction.format(6), \"row2\")\n    return svg()\n\n\n@fixture\ndef row1(svg1):\n    return svg1.g[0]\n\n\n@fixture\ndef row2(svg1):\n    return svg1.g[1]\n\n\n@fixture\ndef instruction1(row1):\n    return row1.g[0]\n\n\n@fixture\ndef instruction2(row1):\n    return row1.g[1]\n\n\n@fixture\ndef instruction3(row1):\n    return row1.g[2]\n\n\n@fixture\ndef instruction21(row2):\n    return row2.g[0]\n\n\n@fixture\ndef instruction22(row2):\n    return row2.g[1]\n\n\n@fixture\ndef instruction23(row2):\n    return row2.g[2]\n\n\ndef test_rendering_nothing_is_a_valid_xml(builder, file):\n    builder.write_to_file(file)\n    file.seek(0)\n    first_line = file.readline()\n    assert first_line.endswith(\"?>\\n\")\n    assert first_line.startswith(\"<?xml\")\n\n\ndef test_rendering_nothing_is_an_svg(builder, file):\n    builder.write_to_file(file)\n    file.seek(0)\n    svg = parse_file(file)\n    with raises(IndexError):\n        svg.g\n\n\ndef test_translate_to_right_position(instruction1):\n    assert instruction1[\"transform\"] == \"translate(0,0)\"\n\n\ndef test_row_has_id(row1):\n    assert row1[\"id\"] == \"row1\"\n\n\ndef test_row_is_displayed_correctly_by_inkscape(row1):\n    assert row1[\"inkscape:label\"] == \"row1\"\n    assert row1[\"inkscape:groupmode\"] == \"layer\"\n\n\ndef test_content_is_in_group(instruction1):\n    assert instruction1.g[\"id\"] == \"inst1-id\"\n\n\ndef test_row_contains_several_instructions(row1, row2):\n    assert len(row1.g) == 3\n    assert len(row2.g) == 3\n\n\ndef test_instruction2_is_translated(instruction2):\n    assert instruction2[\"transform\"] == \"translate(1,0)\"\n\n\ndef test_instruction3_is_translated(instruction3):\n    assert instruction3[\"transform\"] == \"translate(2,0)\"\n\n\ndef test_instruction21_is_translated(instruction21):\n    assert instruction21[\"transform\"] == \"translate(0,1)\"\n\n\ndef test_instruction22_is_translated(instruction22):\n    assert instruction22[\"transform\"] == \"translate(1,1)\"\n\n\ndef test_instruction23_is_translated(instruction23):\n    assert instruction23[\"transform\"] == \"translate(2.0,1.0)\"\n\n\ndef test_bounding_box(builder):\n    assert builder.bounding_box == BBOX\n\n\ndef test_viewport_is_bounding_box(svg1):\n    assert svg1[\"viewBox\"] == \"{} {} {} {}\".format(*BBOX)\n\n\ndef test_width(svg1):\n    assert svg1[\"width\"] == \"{}\".format(BBOX[2] - BBOX[0])\n\n\ndef test_height(svg1):\n    assert svg1[\"height\"] == \"{}\".format(BBOX[3] - BBOX[1])\n"
  },
  {
    "path": "knittingpattern/convert/test/test_convert.py",
    "content": "#\n# see https://pytest.org/latest/goodpractices.html\n# for why this module exists\n#\nfrom pytest import fixture, raises, fail\nfrom unittest.mock import MagicMock, call\nimport pytest\nimport os\nimport sys\nimport io\ntry:\n    import untangle  # http://docs.python-guide.org/en/latest/scenarios/xml/\nexcept ImportError:\n    raise ImportError(\"Install untangle with \\\"{} -m pip install untangle\\\".\"\n                      \"\".format(sys.executable))\n\nHERE = os.path.dirname(__file__)\n\nsys.path.insert(0, os.path.join(HERE, \"../../..\"))\n\n\ndef parse_file(file):\n    parser = untangle.make_parser()\n    sax_handler = untangle.Handler()\n    parser.setContentHandler(sax_handler)\n    parser.parse(file)\n    return sax_handler.root\n\n\ndef parse_string(string):\n    file = io.StringIO()\n    file.write(string)\n    file.seek(0)\n    return parse_file(file)\n\n\n__all__ = [\"HERE\", \"parse_string\", \"parse_file\", \"pytest\", \"fixture\", \"raises\",\n           \"fail\", \"MagicMock\", \"call\", \"untangle\", \"os\", \"sys\", \"io\"]\n"
  },
  {
    "path": "knittingpattern/convert/test/test_default_instruction_layout.py",
    "content": "\"\"\"Test rendering propetries of the default instructions.\"\"\"\nfrom test_convert import fixture\nfrom knittingpattern.InstructionLibrary import default_instructions\n\n\n@fixture\ndef default():\n    return default_instructions()\n\n\ndef test_knit_has_no_z_index(default):\n    assert default[\"knit\"].render_z == 0\n\n\ndef test_yo_has_z_index(default):\n    assert default[\"yo\"].render_z == 1\n    assert default[\"yo twisted\"].render_z == 1\n"
  },
  {
    "path": "knittingpattern/convert/test/test_default_svgs.py",
    "content": "from test_convert import fixture, os, pytest, HERE\nfrom knittingpattern.convert.InstructionToSVG import \\\n    default_instructions_to_svg\nfrom collections import namedtuple\n\nInstruction = namedtuple(\"Instruction\", [\"type\"])\ndefault_files = os.listdir(os.path.join(HERE, \"..\", \"..\", \"instructions\"))\ndefault_types = [os.path.splitext(file)[0] for file in default_files]\n\n\n@fixture(scope=\"module\")\ndef default():\n    return default_instructions_to_svg()\n\n\ndef test_default_instruction_is_not_the_same(default):\n    \"\"\"This allows loading different svgs based on the default set.\"\"\"\n    assert default_instructions_to_svg() != default\n\n\n@pytest.mark.parametrize('instruction', list(map(Instruction, default_types)))\ndef test_instructions_have_svg(default, instruction):\n    assert default.has_svg_for_instruction(instruction)\n\n\ndef test_default_does_not_have_all_instructions(default):\n    assert not default.has_svg_for_instruction(Instruction(\"asjdkalks\"))\n"
  },
  {
    "path": "knittingpattern/convert/test/test_images.py",
    "content": "from test_convert import os, HERE, pytest\nimport re\n\nIMAGES_FOLDER_NAME = \"test_images\"\nIMAGES_FOLDER = os.path.join(HERE, IMAGES_FOLDER_NAME)\nKNIT_FILE = os.path.join(IMAGES_FOLDER, \"knit.svg\")\nPURL_FILE = os.path.join(IMAGES_FOLDER, \"purl.svg\")\nYO_FILE = os.path.join(IMAGES_FOLDER, \"yo.svg\")\nK2TOG_FILE = os.path.join(IMAGES_FOLDER, \"k2tog.svg\")\nDEFAULT_FILE = os.path.join(IMAGES_FOLDER, \"default.svg\")\n\n\ndef title(content):\n    \"\"\"returns the title of the svg\"\"\"\n    if isinstance(content, str):\n        return re.findall(\"<title[^>]*>([^<]*)</title>\", content)[-1]\n    return content.title.cdata\n\n\ndef is_knit(content):\n    return title(content) == \"knit\"\n\n\ndef is_purl(content):\n    return title(content) == \"purl\"\n\n\ndef is_yo(content):\n    return title(content) == \"yo\"\n\n\ndef is_k2tog(content):\n    return title(content) == \"k2tog\"\n\n\ndef is_default(content):\n    return title(content) == \"default\"\n\n\ndef read(path):\n    with open(path) as file:\n        return file.read()\n\n\nfile_to_test = {\n    KNIT_FILE: is_knit,\n    PURL_FILE: is_purl,\n    YO_FILE: is_yo,\n    K2TOG_FILE: is_k2tog,\n    DEFAULT_FILE: is_default\n}\n\n\n@pytest.mark.parametrize('path, test', list(file_to_test.items()))\ndef test_tests_work_on_corresponding_file(path, test):\n    assert test(read(path))\n\n\n@pytest.mark.parametrize('path, test', [\n    (path, _test)\n    for path in file_to_test\n    for test_path, _test in file_to_test.items()\n    if path != test_path\n])\ndef test_tests_do_not_work_on_other_files(path, test):\n    assert not test(read(path))\n\n\ndef test_default_content_has_identifier_in_place():\n    assert \"{instruction.type}\" in read(DEFAULT_FILE)\n\n\n__all__ = [\n    \"KNIT_FILE\", \"PURL_FILE\", \"YO_FILE\", \"K2TOG_FILE\", \"IMAGES_FOLDER\",\n    \"IMAGES_FOLDER_NAME\", \"DEFAULT_FILE\", \"read\", \"title\",\n    \"is_knit\", \"is_purl\", \"is_yo\", \"is_k2tog\", \"is_default\",\n]\n"
  },
  {
    "path": "knittingpattern/convert/test/test_instruction_to_svg.py",
    "content": "from test_images import IMAGES_FOLDER, DEFAULT_FILE,\\\n    IMAGES_FOLDER_NAME, is_knit, is_purl\nfrom test_convert import fixture, parse_string\nfrom knittingpattern.convert.InstructionToSVG import InstructionToSVG\nfrom collections import namedtuple\n\nInstruction = namedtuple(\"TestInstruction\", [\"type\", \"hex_color\"])\nXML_START = '<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n<svg></svg>'\n\n\n@fixture\ndef knit():\n    return Instruction(\"knit\", \"green\")\n\n\n@fixture\ndef purl():\n    return Instruction(\"purl\", \"red\")\n\n\n@fixture\ndef yo():\n    return Instruction(\"yo\", \"brown\")\n\n\n@fixture\ndef its():\n    return InstructionToSVG()\n\n\n@fixture\ndef loaded(its):\n    its.load.folder(IMAGES_FOLDER)\n    return its\n\n\n@fixture\ndef default(its):\n    its.load.path(DEFAULT_FILE)\n    return its\n\n\nclass TestHasSVGForInstruction(object):\n    \"\"\"This tests the `InstructionToSVG.has_instruction_to_svg` method.\"\"\"\n\n    def test_load_from_file(self, its, knit):\n        its.load.relative_file(__name__, IMAGES_FOLDER_NAME + \"/knit.svg\")\n        assert its.has_svg_for_instruction(knit)\n\n    def test_nothing_is_loaded(self, its, knit, purl):\n        assert not its.has_svg_for_instruction(knit)\n        assert not its.has_svg_for_instruction(purl)\n\n    def test_load_from_directory(self, its, knit, purl):\n        its.load.relative_folder(__name__, IMAGES_FOLDER_NAME)\n        assert its.has_svg_for_instruction(knit)\n        assert its.has_svg_for_instruction(purl)\n\n    def test_all_images_are_loaded(self, loaded, knit, purl, yo):\n        assert loaded.has_svg_for_instruction(knit)\n        assert loaded.has_svg_for_instruction(purl)\n        assert loaded.has_svg_for_instruction(yo)\n\n    def test_default_returns_empty_string_if_nothing_is_loaded(self, its,\n                                                               knit):\n        assert its.default_instruction_to_svg(knit) == XML_START\n\n\nclass TestDefaultInstrucionToSVG(object):\n    \"\"\"This tests the `InstructionToSVG.default_instruction_to_svg` method.\"\"\"\n\n    def test_instruction_type_is_replaced_in_default(self, default, knit):\n        assert \"knit\" in default.instruction_to_svg(knit)\n\n    def test_default_instruction_is_used(self, default, purl):\n        default_string = default.default_instruction_to_svg(purl)\n        string = default.instruction_to_svg(purl)\n        assert string == default_string\n\n\ndef is_color_layer(layer):\n    layer_has_label_color = layer[\"inkscape:label\"] == \"color\"\n    layer_is_of_mode_layer = layer[\"inkscape:groupmode\"] == \"layer\"\n    return layer_has_label_color and layer_is_of_mode_layer\n\n\ndef color_layers(svg):\n    return [layer for layer in svg.g if is_color_layer(layer)]\n\n\ndef assert_has_one_colored_layer(svg):\n    assert len(color_layers(svg)) == 1\n\n\ndef assert_fill_has_color_of(svg, instruction):\n    colored_layer = color_layers(svg)[0]\n    element = (colored_layer.rect\n               if \"rect\" in dir(colored_layer)\n               else colored_layer.circle)\n    style = element[\"style\"]\n    assert \"fill:\" + instruction.hex_color in style\n\n\nclass TestInstructionToSVG(object):\n\n    @fixture\n    def knit_svg(self, loaded, knit):\n        return parse_string(loaded.instruction_to_svg(knit)).svg\n\n    @fixture\n    def purl_svg(self, loaded, purl):\n        return parse_string(loaded.instruction_to_svg(purl)).svg\n\n    def test_file_content_is_included(self, knit_svg):\n        assert is_knit(knit_svg)\n\n    def test_file_content_is_purl(self, purl_svg):\n        assert is_purl(purl_svg)\n\n    def test_returned_object_is_svg_with_viewbox(self, knit_svg):\n        assert len(knit_svg[\"viewBox\"].split()) == 4\n\n    def test_there_is_one_color_layer(self, knit_svg):\n        assert_has_one_colored_layer(knit_svg)\n\n    def test_purl_has_one_color_layer(self, purl_svg):\n        assert_has_one_colored_layer(purl_svg)\n\n    def test_fill_in_colored_layer_is_replaced_by_color(self, knit_svg, knit):\n        assert_fill_has_color_of(knit_svg, knit)\n\n    def test_purl_is_colored(self, purl_svg, purl):\n        assert_fill_has_color_of(purl_svg, purl)\n\n    # TODO: test colored layer so it does everything as specified\n"
  },
  {
    "path": "knittingpattern/convert/test/test_knittingpattern_to_png.py",
    "content": "from test_convert import fixture, pytest\nfrom knittingpattern import load_from_relative_file\nimport PIL.Image\n\n\n@fixture(scope=\"module\")\ndef block4x4():\n    return load_from_relative_file(__name__, \"test_patterns/block4x4.json\")\n\n\n@fixture(scope=\"module\")\ndef path(block4x4):\n    return block4x4.to_ayabpng().temporary_path()\n\n\n@fixture(scope=\"module\")\ndef image(path):\n    return PIL.Image.open(path)\n\n\n@pytest.mark.parametrize('xy', [(i, i) for i in range(4)])\ndef test_there_is_a_green_line(image, xy):\n    assert image.getpixel(xy) == (0, 128, 0)\n\n\ndef test_path_ends_with_png(path):\n    assert path.endswith(\".png\")\n"
  },
  {
    "path": "knittingpattern/convert/test/test_layout.py",
    "content": "\"\"\"Test the layout of knitting patterns.\"\"\"\nfrom test_convert import fixture\nimport os\nfrom knittingpattern.convert.Layout import GridLayout, InstructionInGrid\nfrom knittingpattern import load_from_relative_file\nfrom collections import namedtuple\n\n\ndef coordinates(layout):\n    \"\"\"The coordinates of the layout.\"\"\"\n    return list(layout.walk_instructions(lambda point: (point.x, point.y)))\n\n\ndef sizes(layout):\n    \"\"\"The sizes of the instructions of the layout.\"\"\"\n    return list(layout.walk_instructions(lambda p: (p.width, p.height)))\n\n\ndef instructions(layout):\n    \"\"\"The instructions of the layout.\"\"\"\n    return list(layout.walk_instructions(lambda point: point.instruction))\n\n\ndef row_ids(layout):\n    \"\"\"The ids of the rows of the layout.\"\"\"\n    return list(layout.walk_rows(lambda row: row.id))\n\n\ndef connections(layout):\n    \"\"\"The connections between the rows of the leyout.\"\"\"\n    return list(layout.walk_connections(lambda c: (c.start.xy, c.stop.xy)))\n\n\nclass BaseTest:\n\n    \"\"\"Base class for a set of tests on knitting patterns.\"\"\"\n\n    FILE = \"block4x4.json\"\n    PATTERN = \"knit\"\n    COORDINATES = [(x, y) for y in range(4) for x in range(4)]\n    SIZES = [(1, 1)] * 16\n    ROW_IDS = [1, 2, 3, 4]\n    LARGER_CONNECTIONS = []\n    BOUNDING_BOX = (0, 0, 4, 4)\n\n    @fixture(scope=\"class\")\n    def pattern(self):\n        \"\"\"Teh pattern to test.\"\"\"\n        path = os.path.join(\"test_patterns\", self.FILE)\n        pattern_set = load_from_relative_file(__name__, path)\n        return pattern_set.patterns[self.PATTERN]\n\n    @fixture(scope=\"class\")\n    def grid(self, pattern):\n        \"\"\"The computed grid for the pattern.\"\"\"\n        return GridLayout(pattern)\n\n    def test_coordinates(self, grid):\n        \"\"\"Test the coordinates of the layout.\"\"\"\n        coords = coordinates(grid)\n        print(\"generated:\", coords)\n        print(\"expected: \", self.COORDINATES)\n        assert coords == self.COORDINATES\n\n    def test_size(self, grid):\n        \"\"\"Test teh sizes of the layout.\"\"\"\n        generated = sizes(grid)\n        print(\"generated:\", generated)\n        print(\"expected: \", self.SIZES)\n        assert generated == self.SIZES\n\n    def test_instructions(self, grid, pattern):\n        \"\"\"Test that the instructions are returned row-wise.\"\"\"\n        instructions_ = []\n        for row_id in self.ROW_IDS:\n            for instruction in pattern.rows[row_id].instructions:\n                instructions_.append(instruction)\n        assert instructions(grid) == instructions_\n\n    def test_row_ids(self, grid):\n        \"\"\"Test the order of rows.\"\"\"\n        assert row_ids(grid) == self.ROW_IDS\n\n    def test_connections(self, grid):\n        \"\"\"test the connections betwen rows.\"\"\"\n        generated = connections(grid)\n        print(\"generated:\", generated)\n        print(\"expected: \", self.LARGER_CONNECTIONS)\n        assert generated == self.LARGER_CONNECTIONS\n\n    def test_bounding_box(self, grid):\n        \"\"\"Test the bounding box of the layout.\"\"\"\n        assert grid.bounding_box == self.BOUNDING_BOX\n\n\nclass TestBlock4x4(BaseTest):\n    \"\"\"Execute the BaseTest.\"\"\"\n\n\nclass TestHole(BaseTest):\n    \"\"\"Test if holes are layouted.\"\"\"\n    FILE = \"with hole.json\"\n    SIZES = BaseTest.SIZES[:]\n    SIZES[5] = (2, 1)\n    SIZES[6] = (0, 1)\n    COORDINATES = BaseTest.COORDINATES[:]\n    COORDINATES[6] = COORDINATES[7]\n\n\nclass TestAddAndRemoveMeshes(BaseTest):\n    \"\"\"take away and add meshes at the right and left.\"\"\"\n    FILE = \"add and remove meshes.json\"\n    SIZES = [(1, 1)] * 17\n    COORDINATES = [\n        (0, 0), (1, 0), (2, 0), (3, 0),\n        (0, 1), (1, 1), (2, 1), (3, 1), (4, 1),\n        (0, 2), (1, 2), (2, 2),\n        (-1, 3), (0, 3), (1, 3), (2, 3), (3, 3)\n    ]\n    BOUNDING_BOX = (-1, 0, 5, 4)\n\n    # test how instructions are connected\n\n    @fixture\n    def i_1(self, pattern):\n        return pattern.rows[1].instructions\n\n    @fixture\n    def i_2(self, pattern):\n        return pattern.rows[2].instructions\n\n    @fixture\n    def i_3(self, pattern):\n        return pattern.rows[3].instructions\n\n    @fixture\n    def i_4(self, pattern):\n        return pattern.rows[4].instructions\n\n    @fixture\n    def instructions(self, i_1, i_2, i_3, i_4):\n        return i_1 + i_2 + i_3 + i_4\n\n    def test_all_consume_one_mesh(self, instructions):\n        assert all(i.number_of_consumed_meshes == 1\n                   for i in instructions)\n\n    def test_all_produce_one_mesh(self, instructions):\n        assert all(i.number_of_produced_meshes == 1\n                   for i in instructions)\n\n    # i_1 produced\n\n    def test_i_1_0_is_not_produced(self, i_1):\n        assert i_1[0].producing_instructions == [None]\n\n    def test_i_1_1_is_not_produced(self, i_1):\n        assert i_1[1].producing_instructions == [None]\n\n    def test_i_1_2_is_not_produced(self, i_1):\n        assert i_1[2].producing_instructions == [None]\n\n    def test_i_1_3_is_not_produced(self, i_1):\n        assert i_1[3].producing_instructions == [None]\n\n    # i_1 consumed\n\n    def test_i_1_0_consumed(self, i_1, i_2):\n        assert i_1[0].consuming_instructions == [i_2[0]]\n\n    def test_i_1_1_consumed(self, i_1, i_2):\n        assert i_1[1].consuming_instructions == [i_2[1]]\n\n    def test_i_1_2_consumed(self, i_1, i_2):\n        assert i_1[2].consuming_instructions == [i_2[2]]\n\n    def test_i_1_3_consumed(self, i_1, i_2):\n        assert i_1[3].consuming_instructions == [i_2[3]]\n\n    # i_2 produced\n\n    def test_i_2_0_produced(self, i_1, i_2):\n        assert i_2[0].producing_instructions == [i_1[0]]\n\n    def test_i_2_1_produced(self, i_1, i_2):\n        assert i_2[1].producing_instructions == [i_1[1]]\n\n    def test_i_2_2_produced(self, i_1, i_2):\n        assert i_2[2].producing_instructions == [i_1[2]]\n\n    def test_i_2_3_produced(self, i_1, i_2):\n        assert i_2[3].producing_instructions == [i_1[3]]\n\n    def test_i_2_4_produced(self, i_2):\n        assert i_2[4].producing_instructions == [None]\n\n    # i_2 consumed\n\n    def test_i_2_0_consumed(self, i_2, i_3):\n        assert i_2[0].consuming_instructions == [i_3[0]]\n\n    def test_i_2_1_consumed(self, i_2, i_3):\n        assert i_2[1].consuming_instructions == [i_3[1]]\n\n    def test_i_2_2_consumed(self, i_2, i_3):\n        assert i_2[2].consuming_instructions == [i_3[2]]\n\n    def test_i_2_3_not_consumed(self, i_2):\n        assert i_2[3].consuming_instructions == [None]\n\n    def test_i_2_4_not_consumed(self, i_2):\n        assert i_2[4].consuming_instructions == [None]\n\n    # i_3 produced\n\n    def test_i_3_0_produced(self, i_2, i_3):\n        assert i_3[0].producing_instructions == [i_2[0]]\n\n    def test_i_3_1_produced(self, i_2, i_3):\n        assert i_3[1].producing_instructions == [i_2[1]]\n\n    def test_i_3_2_produced(self, i_2, i_3):\n        assert i_3[2].producing_instructions == [i_2[2]]\n\n    # i_3 consumed\n\n    def test_i_3_0_consumed(self, i_3, i_4):\n        assert i_3[0].consuming_instructions == [i_4[1]]\n\n    def test_i_3_1_consumed(self, i_3, i_4):\n        assert i_3[1].consuming_instructions == [i_4[2]]\n\n    def test_i_3_2_consumed(self, i_3, i_4):\n        assert i_3[2].consuming_instructions == [i_4[3]]\n\n    # i_4 produced\n\n    def test_i_4_0_not_produced(self, i_4):\n        assert i_4[0].producing_instructions == [None]\n\n    def test_i_4_1_produced(self, i_3, i_4):\n        assert i_4[1].producing_instructions == [i_3[0]]\n\n    def test_i_4_2_produced(self, i_3, i_4):\n        assert i_4[2].producing_instructions == [i_3[1]]\n\n    def test_i_4_3_produced(self, i_3, i_4):\n        assert i_4[3].producing_instructions == [i_3[2]]\n\n    def test_i_4_4_not_produced(self, i_4):\n        assert i_4[4].producing_instructions == [None]\n\n    # i_4 consumed\n\n    def test_i_4_0_not_consumed(self, i_4):\n        assert i_4[0].consuming_instructions == [None]\n\n    def test_i_4_1_not_consumed(self, i_4):\n        assert i_4[1].consuming_instructions == [None]\n\n    def test_i_4_2_not_consumed(self, i_4):\n        assert i_4[2].consuming_instructions == [None]\n\n    def test_i_4_3_not_consumed(self, i_4):\n        assert i_4[3].consuming_instructions == [None]\n\n    def test_i_4_4_not_consumed(self, i_4):\n        assert i_4[4].consuming_instructions == [None]\n\n\nclass TestParallelRows(BaseTest):\n    \"\"\"Test unconnected rows of different sizes.\"\"\"\n    FILE = \"split_up_and_add_rows.json\"\n    SIZES = [(1, 1)] * 15\n    SIZES[-2] = (2, 1)\n    COORDINATES = [\n        (0, 0), (1, 0), (2, 0), (3, 0), (4, 0),\n        (3, 1), (4, 1),\n        (0, 2), (1, 2),  # could also be (0, 1), (1, 1)\n        (3, 2), (4, 2),\n        (0, 3), (1, 3), (2, 3), (4, 3)\n    ]\n    ROW_IDS = [\"1.1\", \"2.2\", \"2.1\", \"3.2\", \"4.1\"]\n    # LARGER_CONNECTIONS = [((0, 1), (0, 3)), ((1, 1), (1, 3))]\n    LARGER_CONNECTIONS = [((0, 0), (0, 2)), ((1, 0), (1, 2))]\n    BOUNDING_BOX = (0, 0, 5, 4)\n\n    @fixture\n    def row_4(self, pattern):\n        return pattern.rows[\"4.1\"]\n\n    @fixture\n    def skp(self, row_4):\n        return row_4.instructions[2]\n\n    def test_skp_has_2_consumed_meshes(self, skp):\n        \"\"\"Test skp consumes two meshes.\"\"\"\n        assert skp.type == \"skp\"\n        assert skp.number_of_consumed_meshes == 2\n\n    def test_row_4_1_consumes_5_meshes(self, row_4):\n        \"\"\"Test: the yo in the last row adds to the skp so the width is 5.\"\"\"\n        assert row_4.number_of_consumed_meshes == 5\n        assert len(row_4.consumed_meshes) == 5\n\nInstruction = namedtuple(\"Instruction\", [\"color\", \"number_of_consumed_meshes\"])\n\n\ndef test_get_color_from_instruction_in_grid():\n    \"\"\"Test the color attribute.\"\"\"\n    instruction = Instruction(\"black\", 1)\n    instruction_in_grid = InstructionInGrid(instruction, (0, 0))\n    assert instruction_in_grid.color == \"black\"\n\n\nclass TestSmallCafe(BaseTest):\n    \"\"\"This test tests the negative expansion of rows.\n\n    If you start expanding in the middle, this tests that all rows are\n    included.\n    \"\"\"\n    FILE = \"small-cafe.json\"\n    PATTERN = \"A.2\"\n    SIZES = \\\n        [(1, 1)] * 12 + \\\n        [(0, 1), (1, 1), (0, 1)] + [(1, 1)] * 11 + \\\n        [(1, 1)] * 14 + \\\n        [(1, 1)] * 3 + [(0, 1)] + [(1, 1)] * 11 + [(0, 1)] + \\\n        [(1, 1)] * 16\n    COORDINATES = \\\n        [(i, -1) for i in range(12)] + \\\n        [(0, 0), (0, 0), (1, 0)] + [(i, 0) for i in range(1, 12)] + \\\n        [(i, 1) for i in range(-1, 13)] + \\\n        [(-1, 2), (0, 2), (1, 2), (2, 2)] + [(i, 2) for i in range(2, 13)] + \\\n        [(13, 2)] + \\\n        [(i, 3) for i in range(-2, 14)]\n    ROW_IDS = [\"B.first\", \"A.2.25\", \"A.2.26\", \"A.2.27\", \"A.2.28\"]\n    LARGER_CONNECTIONS = []\n    BOUNDING_BOX = (-2, -1, 15, 4)\n\n\nclass TestCastOffAndBindOn(BaseTest):\n    \"\"\"Cast On and Bind off have no size but must be layouted differently.\"\"\"\n    FILE = \"cast_on_and_bind_off.json\"\n    SIZES = [(1, 1)] * 12\n    COORDINATES = [(x, y) for y in range(3) for x in range(4)]\n    ROW_IDS = [1, 2, 3]\n    LARGER_CONNECTIONS = []\n    BOUNDING_BOX = (0, 0, 4, 3)\n\n    @fixture\n    def cast_on(self, co_row):\n        return co_row.instructions[0]\n\n    @fixture\n    def co_row(self, pattern):\n        return pattern.rows[1]\n\n    @fixture\n    def co_row_in_grid(self, grid, co_row):\n        return grid.row_in_grid(co_row)\n\n    def test_cast_on_has_layout_specific_width(self, cast_on):\n        \"\"\"Test that cast On has a custom width.\"\"\"\n        assert cast_on[\"grid-layout\"][\"width\"] == 1\n\n    def test_first_row_has_width_4(self, co_row_in_grid):\n        \"\"\"Test that the Cast On row has a width.\"\"\"\n        assert co_row_in_grid.width == 4\n\n\n# TODO\n#\n# def test_use_row_with_lowest_number_of_incoming_connections_as_first_row():\n#     fail()\n#\n#\n# def test_if_row_with_lowest_number_of_connections_exist_use_smallest_id():\n#     fail()\n"
  },
  {
    "path": "knittingpattern/convert/test/test_patterns/add and remove meshes.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"4x4 meshes block\",\n    \"type\" : \"markdown\"\n    },\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"add and remove meshes\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"id\": \"1.0\"},\n            {\"id\": \"1.1\"},\n            {\"id\": \"1.2\"},\n            {\"id\": \"1.3\"}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"id\": \"2.0\"},\n            {\"id\": \"2.1\"},\n            {\"id\": \"2.2\"},\n            {\"id\": \"2.3\"},\n            {\"id\": \"2.5\"}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"id\": \"3.0\"},\n            {\"id\": \"3.1\"},\n            {\"id\": \"3.2\"}\n          ]\n        },\n        {\n          \"id\" : 4,\n          \"instructions\" : [\n            {\"id\": \"4.-1\"},\n            {\"id\": \"4.0\"},\n            {\"id\": \"4.1\"},\n            {\"id\": \"4.2\"},\n            {\"id\": \"4.3\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 1\n          }, \n          \"to\" : {\n            \"id\" : 2\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          }, \n          \"to\" : {\n            \"id\" : 3\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 3\n          }, \n          \"to\" : {\n            \"start\" : 1,\n            \"id\" : 4\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/convert/test/test_patterns/block4x4.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"4x4 meshes block\",\n    \"type\" : \"markdown\"\n    },\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"knit 4x4\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"id\": \"1.0\", \"color\": \"green\"},\n            {\"id\": \"1.1\"},\n            {\"id\": \"1.2\"},\n            {\"id\": \"1.3\"}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"id\": \"3.0\"},\n            {\"id\": \"3.1\"},\n            {\"id\": \"3.2\", \"color\": \"green\"},\n            {\"id\": \"3.3\"}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"id\": \"2.0\"},\n            {\"id\": \"2.1\", \"color\": \"green\"},\n            {\"id\": \"2.2\"},\n            {\"id\": \"2.3\"}\n          ]\n        },\n        {\n          \"id\" : 4,\n          \"instructions\" : [\n            {\"id\": \"4.0\"},\n            {\"id\": \"4.1\"},\n            {\"id\": \"4.2\"},\n            {\"id\": \"4.3\", \"color\": \"green\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 1\n          },\n          \"to\" : {\n            \"id\" : 2\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          },\n          \"to\" : {\n            \"id\" : 3\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 3\n          },\n          \"to\" : {\n            \"id\" : 4\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/convert/test/test_patterns/cast_on_and_bind_off.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"cast on and bind off\",\n    \"type\" : \"markdown\"\n    },\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"cobo\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"id\": \"1.0\", \"type\": \"co\"},\n            {\"id\": \"1.1\", \"type\": \"co\"},\n            {\"id\": \"1.2\", \"type\": \"co\"},\n            {\"id\": \"1.3\", \"type\": \"co\"}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"id\": \"3.0\"},\n            {\"id\": \"3.1\"},\n            {\"id\": \"3.2\"},\n            {\"id\": \"3.3\"}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"id\": \"2.0\", \"type\": \"bo\"},\n            {\"id\": \"2.1\", \"type\": \"bo\"},\n            {\"id\": \"2.2\", \"type\": \"bo\"},\n            {\"id\": \"2.3\", \"type\": \"bo\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 1\n          }, \n          \"to\" : {\n            \"id\" : 2\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          }, \n          \"to\" : {\n            \"id\" : 3\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/convert/test/test_patterns/small-cafe.json",
    "content": "{\n  \"version\" : \"0.1\",\n  \"type\" : \"knitting pattern\", \n  \"comment\" : {\n    \"markdown\" : \"This pattern is taken from [garnstudio](http://www.garnstudio.com/pattern.php?id=7350&cid=19). It only contains the last 4 rows\",\n    \"picture\" : {\n      \"type\" : \"url\",\n      \"url\" : \"http://www.garnstudio.com/drops/mag/167/17/17-lp.jpg\"\n    }\n  },\n  \"patterns\" : [\n    {\n      \"name\" : \"A.2\", \n      \"id\" : \"A.2\", \n      \"rows\" : [\n        {\n          \"id\" : \"A.2.25\", \n          \"color\" : \"light brown\",\n          \"instructions\" : [\n            {\n              \"type\" : \"yo\"\n            }, {}, {\n              \"type\" : \"yo\"\n            }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}\n          ]\n        },\n        {\n          \"id\" : \"A.2.26\", \n          \"color\" : \"light brown\",\n          \"instructions\" : [\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            }\n          ]\n        }, \n        {\n          \"id\" : \"A.2.27\", \n          \"color\" : \"light brown\",\n          \"instructions\" : [\n            {}, {}, {}, {\n              \"type\" : \"yo\"\n            }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {\n              \"type\" : \"yo\"\n            }\n          ]\n        },\n        {\n          \"id\" : \"A.2.28\", \n          \"color\" : \"light brown\",\n          \"instructions\" : [\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            }\n          ]\n        },\n        {\n          \"id\" : \"B.first\", \n          \"color\" : \"light brown\",\n          \"instructions\" : [\n            {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : \"B.first\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.25\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.25\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.26\",\n            \"start\" : 1\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.26\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.27\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.27\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.28\",\n            \"start\" : 1\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/convert/test/test_patterns/split_up_and_add_rows.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"A.1\",\n      \"rows\" : [\n        {\n          \"id\" : \"1.1\",\n          \"instructions\" : [\n            {\"id\": \"1.1.0\"},\n            {\"id\": \"1.1.1\"},\n            {\"id\": \"1.1.2\"},\n            {\"id\": \"1.1.3\"},\n            {\"id\": \"1.1.4\"}\n          ]\n        },\n        {\n          \"id\" : \"2.1\",\n          \"instructions\" : [\n            {\"id\": \"2.1.0\"},\n            {\"id\": \"2.1.1\"}\n          ]\n        },\n        {\n          \"id\" : \"2.2\",\n          \"instructions\" : [\n            {\"id\": \"2.2.0\"},\n            {\"id\": \"2.2.1\"}\n          ]\n        },\n        {\n          \"id\" : \"3.2\",\n          \"instructions\" : [\n            {\"id\": \"3.2.0\"},\n            {\"id\": \"3.2.1\"}\n          ]\n        },\n        {\n          \"id\" : \"4.1\",\n          \"instructions\" : [\n            {\"id\": \"4.1.0\"},\n            {\"id\": \"4.1.1\"},\n            {\"id\": \"4.1.2\", \"type\": \"skp\"},\n            {\"id\": \"4.1.4\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : \"1.1\",\n            \"start\" : 0\n          }, \n          \"to\" : {\n            \"id\" : \"2.1\",\n            \"start\" : 0\n          },\n          \"meshes\" : 2\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"1.1\",\n            \"start\" : 3\n          }, \n          \"to\" : {\n            \"id\" : \"2.2\",\n            \"start\" : 0\n          },\n          \"meshes\" : 2\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"2.2\",\n            \"start\" : 0\n          }, \n          \"to\" : {\n            \"id\" : \"3.2\",\n            \"start\" : 0\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"2.1\"\n          }, \n          \"to\" : {\n            \"id\" : \"4.1\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"3.2\",\n            \"start\" : 0\n          }, \n          \"to\" : {\n            \"id\" : \"4.1\",\n            \"start\" : 3\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/convert/test/test_patterns/with hole.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"4x4 meshes block\",\n    \"type\" : \"markdown\" \n    },\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"knit 4x4\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"id\": \"1.0\"},\n            {\"id\": \"1.1\"},\n            {\"id\": \"1.2\"},\n            {\"id\": \"1.3\"}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"id\": \"2.0\"},\n            {\"id\": \"2.1\", \"type\": \"skp\"},\n            {\"id\": \"2.2\", \"type\": \"yo\"},\n            {\"id\": \"2.3\"}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"id\": \"3.0\"},\n            {\"id\": \"3.1\"},\n            {\"id\": \"3.2\"},\n            {\"id\": \"3.3\"}\n          ]\n        },\n        {\n          \"id\" : 4,\n          \"instructions\" : [\n            {\"id\": \"4.0\"},\n            {\"id\": \"4.1\"},\n            {\"id\": \"4.2\"},\n            {\"id\": \"4.3\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 1\n          }, \n          \"to\" : {\n            \"id\" : 2\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          }, \n          \"to\" : {\n            \"id\" : 3\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 3\n          }, \n          \"to\" : {\n            \"id\" : 4\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/convert/test/test_png_to_knittingpattern.py",
    "content": "from test_convert import fixture, HERE, os\nfrom knittingpattern.convert.image_to_knittingpattern import \\\n    convert_image_to_knitting_pattern\nfrom knittingpattern import convert_from_image\nfrom PIL import Image\n\n\nIMAGE_PATH = os.path.join(HERE, \"pictures\")\n\n\n@fixture(scope=\"module\")\ndef patterns(image_path, convert):\n    loader = convert()\n    return loader.path(image_path).knitting_pattern()\n\n\n@fixture(scope=\"module\")\ndef pattern(patterns):\n    return patterns.patterns.at(0)\n\n\n@fixture(scope=\"module\")\ndef image(image_path):\n    return Image.open(image_path)\n\n\ndef pytest_generate_tests(metafunc):\n    if \"image_path\" in metafunc.fixturenames:\n        metafunc.parametrize(\"image_path\", [\n            os.path.join(IMAGE_PATH, file)\n            for file in os.listdir(IMAGE_PATH)\n            if file.startswith(\"conversion\")], scope=\"module\")\n    if \"convert\" in metafunc.fixturenames:\n        metafunc.parametrize(\"convert\", [convert_image_to_knitting_pattern,\n                                         convert_from_image], scope=\"module\")\n\n\ndef test_convert_image_to_knittingpattern(patterns, image_path):\n    assert patterns.comment[\"source\"] == image_path\n\n\ndef test_row_length_is_image_length(pattern, image):\n    min_x, min_y, max_x, max_y = image.getbbox()\n    assert len(pattern.rows.at(0).instructions) == max_x - min_x\n\n\ndef test_first_color_is_white(pattern):\n    assert pattern.rows[0].instructions[0].color == \"white\"\n\n\ndef test_other_color_is_white(pattern):\n    assert pattern.rows[1].instructions[1].color == \"white\"\n\n\ndef test_black_exists(pattern):\n    assert pattern.rows[20].instructions[64].color == \"black\"\n\n\ndef test_order_of_conversion():\n    loader = convert_from_image()\n    dumper = loader.relative_file(HERE, \"pictures/color-order.png\")\n    patterns = dumper.knitting_pattern()\n    pattern = patterns.patterns.at(0)\n    row1, row2, row3 = pattern.rows\n    assert row1.first_instruction.color == row2.first_instruction.color\n    assert row2.first_instruction.color != row3.first_instruction.color\n"
  },
  {
    "path": "knittingpattern/convert/test/test_save_as_svg.py",
    "content": "from test_convert import fixture\nfrom knittingpattern import load_from_relative_file\nimport untangle\nfrom itertools import chain\nimport re\n\nINKSCAPE_MESSAGE = \"row is usable by inkscape\"\nTRANSFORM_REGEX = \"^translate\\(\\s*(\\S+?)\\s*,\\s*(\\S+?)\\s*\\)\\s*,\"\\\n                  \"\\s*scale\\(\\s*(\\S+?)\\s*\\)$\"\nZOOM_MESSAGE = \"zoom is computed from height\"\nDEFAULT_ZOOM = 25\n\n\ndef is_close_to(v1, v2, relative_epsilon=0.01):\n    return v2 * (1 - relative_epsilon) < v1 < v2 * (1 + relative_epsilon)\n\n\n@fixture(scope=\"module\")\ndef patterns_svg():\n    return load_from_relative_file(__name__, \"test_patterns/block4x4.json\")\n\n\n@fixture(scope=\"module\")\ndef path(patterns_svg):\n    return patterns_svg.to_svg(zoom=DEFAULT_ZOOM).temporary_path(\".svg\")\n\n\n@fixture(scope=\"module\")\ndef svg(path):\n    return untangle.parse(path).svg\n\n\n@fixture\ndef rows(svg):\n    return [(\"row-{}\".format(i), row) for i, row in enumerate(svg.g, 1)]\n\n\n@fixture\ndef instructions(rows):\n    return chain(*(row.g for _, row in rows))\n\n\ndef rows_test(function):\n    def test(rows):\n        for row_id, row in rows:\n            function(row_id, row)\n    return test\n\n\ndef instructions_test(function):\n    def test(instructions):\n        for instruction in instructions:\n            function(instruction)\n    return test\n\n\ndef instructions_svg_test(function):\n    def test(instructions):\n        for instruction in instructions:\n            function(instruction, svg, path, patterns_svg)\n    return test\n\n\ndef test_svg_contains_four_rows(svg):\n    assert len(svg.g) == 4\n\n\n@rows_test\ndef test_rows_contain_four_instructions(row_id, row):\n    assert len(row.g) == 4\n\n\n@rows_test\ndef test_rows_are_labeled_for_inkscape(row_id, row):\n    assert row[\"inkscape:label\"] == row_id\n\n\n@rows_test\ndef test_row_is_inkscape_layer(row_id, row):\n    assert row[\"inkscape:groupmode\"] == \"layer\"\n\n\n@rows_test\ndef test_rows_have_class_for_styling(row_id, row):\n    assert row[\"class\"] == \"row\"\n\n\n@rows_test\ndef test_rows_have_id_for_styling(row_id, row):\n    assert row[\"id\"] == row_id\n\n\n@instructions_test\ndef test_instructions_have_class(instruction):\n    assert instruction[\"class\"] == \"instruction\"\n\n\n@instructions_test\ndef test_instructions_have_id(instruction):\n    # TODO all ids should be made up from the real ids\n    assert instruction[\"id\"].startswith(\"instruction-\")\n\n\n@instructions_test\ndef test_instructions_content_is_knit_svg_file(instruction):\n    assert instruction.use[\"xlink:href\"].startswith(\"#knit\")\n\n\n@instructions_svg_test\ndef test_instructions_have_transform(instruction, svg, path, patterns_svg):\n    transform = instruction[\"transform\"]\n    x, y, zoom = map(float, re.match(TRANSFORM_REGEX, transform).groups())\n    bbox = list(map(float, svg(path(patterns_svg()))[\"viewBox\"].split()))\n    assert is_close_to(DEFAULT_ZOOM / (bbox[3] - bbox[1]), zoom), ZOOM_MESSAGE\n"
  },
  {
    "path": "knittingpattern/examples/Cafe.json",
    "content": "{\n  \"version\" : \"0.1\",\n  \"type\" : \"knitting pattern\", \n  \"comment\" : {\n    \"markdown\" : \"This pattern is taken from [garnstudio](http://www.garnstudio.com/pattern.php?id=7350&cid=19).\",\n    \"picture\" : {\n      \"type\" : \"url\",\n      \"url\" : \"http://www.garnstudio.com/drops/mag/167/17/17-lp.jpg\"\n    }\n  },\n  \"patterns\" : [\n    {\n      \"name\" : \"A.2\", \n      \"id\" : \"A.2\", \n      \"rows\" : [\n        {\n          \"id\" : \"A.2.1\",\n          \"color\" : \"mocha latte\",\n          \"instructions\" : [\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {}, \n            {},\n            {}, \n            {},\n            {\n              \"type\" : \"cdd\"\n            }, \n            {},\n            {},\n            {},\n            {},\n            {\n              \"type\" : \"yo\"\n            }\n          ]\n        }, \n        {\n          \"id\" : \"A.2.2\", \n          \"color\" : \"mocha latte\", \n          \"instructions\" : [\n            {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}\n          ]\n        }, \n        {\n          \"id\" : \"A.2.3\",\n          \"color\" : \"dark brown\",\n          \"instructions\" : [\n            {},\n            {}, \n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {}, \n            {},\n            {\n              \"type\" : \"cdd\"\n            }, \n            {},\n            {},\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {}\n          ]\n        },\n        {\n          \"id\" : \"A.2.4\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.2\"\n        }, \n        {\n          \"id\" : \"A.2.5\",\n          \"color\" : \"dark brown\",\n          \"instructions\" : [\n            {},\n            {}, \n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {}, \n            {},\n            {\n              \"type\" : \"cdd\"\n            }, \n            {},\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {}\n          ]\n        },\n        {\n          \"id\" : \"A.2.6\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.2\"\n        }, \n        {\n          \"id\" : \"A.2.7\",\n          \"color\" : \"brown\",\n          \"instructions\" : [\n            {},\n            {}, \n            {},\n            {}, \n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"cdd\"\n            }, \n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {},\n            {}\n          ]\n        },\n        {\n          \"id\" : \"A.2.8\", \n          \"color\" : \"brown\",\n          \"same as\" : \"A.2.2\"\n        }, \n        {\n          \"id\" : \"A.2.9\",\n          \"color\" : \"brown\",\n          \"instructions\" : [\n            {},\n            {}, \n            {},\n            {}, \n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {\n              \"type\" : \"cdd\"\n            }, \n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {},\n            {},\n            {}\n          ]\n        },\n        {\n          \"id\" : \"A.2.10\", \n          \"color\" : \"brown\",\n          \"same as\" : \"A.2.2\"\n        }, \n        {\n          \"id\" : \"A.2.11\", \n          \"same as\" : \"A.2.10\"\n        }, \n        {\n          \"id\" : \"A.2.12\", \n          \"color\" : \"brown\",\n          \"instructions\" : [\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            }\n          ]\n        }, \n        {\n          \"id\" : \"A.2.13\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.2\"\n        },\n        {\n          \"id\" : \"A.2.14\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.12\"\n        },\n        {\n          \"id\" : \"A.2.15\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.1\"\n        },\n        {\n          \"id\" : \"A.2.16\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.2\"\n        },       \n        {\n          \"id\" : \"A.2.17\", \n          \"color\" : \"white\",\n          \"same as\" : \"A.2.3\"\n        },\n        {\n          \"id\" : \"A.2.18\", \n          \"color\" : \"white\",\n          \"same as\" : \"A.2.2\"\n        },       \n        {\n          \"id\" : \"A.2.19\", \n          \"color\" : \"white\",\n          \"same as\" : \"A.2.5\"\n        },\n        {\n          \"id\" : \"A.2.20\", \n          \"color\" : \"white\",\n          \"same as\" : \"A.2.2\"\n        },       \n        {\n          \"id\" : \"A.2.21\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.7\"\n        },\n        {\n          \"id\" : \"A.2.22\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.2\"\n        },       \n        {\n          \"id\" : \"A.2.23\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.9\"\n        },\n        {\n          \"id\" : \"A.2.24\", \n          \"color\" : \"dark brown\",\n          \"same as\" : \"A.2.2\"\n        },       \n        {\n          \"id\" : \"A.2.25\", \n          \"color\" : \"mocha latte\",\n          \"instructions\" : [\n            {\n              \"type\" : \"yo\"\n            }, {}, {\n              \"type\" : \"yo\"\n            }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}\n          ]\n        },\n        {\n          \"id\" : \"A.2.26\", \n          \"color\" : \"mocha latte\",\n          \"instructions\" : [\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            }\n          ]\n        }, \n        {\n          \"id\" : \"A.2.27\", \n          \"color\" : \"mocha latte\",\n          \"instructions\" : [\n            {}, {}, {}, {\n              \"type\" : \"yo\"\n            }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {\n              \"type\" : \"yo\"\n            }\n          ]\n        },\n        {\n          \"id\" : \"A.2.28\", \n          \"color\" : \"mocha latte\",\n          \"instructions\" : [\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            },\n            {\n              \"type\" : \"purl\"\n            }\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : \"A.2.1\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.2\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.2\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.3\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.3\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.4\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.4\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.5\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.5\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.6\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.6\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.7\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.7\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.8\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.8\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.9\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.9\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.10\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.10\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.11\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.11\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.12\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.12\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.13\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.13\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.14\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.14\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.15\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.15\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.16\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.16\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.17\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.17\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.18\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.18\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.19\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.19\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.20\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.20\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.21\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.21\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.22\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.22\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.23\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.23\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.24\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.24\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.25\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.25\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.26\",\n            \"start\" : 1\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.26\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.27\"\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : \"A.2.27\"\n          }, \n          \"to\" : {\n            \"id\" : \"A.2.28\",\n            \"start\" : 1\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/examples/Charlotte.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"This knitting pattern was taken from [Garnstudio](http://www.garnstudio.com/pattern.php?id=7342&cid=9).\",\n    \"type\" : \"markdown\", \n    \"picture\" : {\n      \"url\" : \"http://www.garnstudio.com/drops/mag/168/15/15-lp.jpg\"\n    }\n  },\n  \"patterns\" : [\n    {\n      \"id\" : \"A.1\",\n      \"name\" : \"A.1\",\n      \"rows\" : [\n        {\n          \"id\" : [\"A.1\", \"empty\", \"1\"],\n          \"instructions\" : [\n            {},\n            {},\n            {},\n            {},\n            {}\n          ]\n        },\n        {\n          \"id\" : [\"A.1\", \"empty\", \"2\"],\n          \"same as\" : [\"A.1\", \"empty\", \"1\"]\n        },\n        {\n          \"id\" : [\"A.1\", \"lace\", \"1\"],\n          \"instructions\" : [\n            {\n              \"type\" : \"skp\"\n            },\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {\n              \"type\" : \"ktog tbl\"\n            }\n          ]\n        }\n      ], \n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : [\"A.1\", \"empty\", \"1\"]\n          }, \n          \"to\" : {\n            \"id\" : [\"A.1\", \"empty\", \"2\"]\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : [\"A.1\", \"empty\", \"2\"]\n          }, \n          \"to\" : {\n            \"id\" : [\"A.1\", \"lace\", \"1\"]\n          }\n        }\n      ]\n    }, \n    {\n      \"id\" : \"A.2\", \n      \"name\" : \"A.2\", \n      \"rows\" : [\n        {\n          \"id\" : [\"A.2\", \"empty\", \"1\"],\n          \"instructions\" : [\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {}\n          ] \n        },\n        {\n          \"id\" : [\"A.2\", \"empty\", \"2\"],\n          \"instructions\" : [\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {},\n            {}\n          ]\n        },\n        {\n          \"id\" : [\"A.2\", \"lace\", \"1\"],\n          \"instructions\" : [\n            {\n              \"type\" : \"skp\",\n              \"count\" : 4\n            },\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {},\n            {\n              \"type\" : \"yo\"\n            },\n            {\n              \"type\" : \"ktog tbl\",\n              \"count\" : 4\n            }\n          ] \n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : [\"A.2\", \"empty\", \"1\"]\n          }, \n          \"to\" : {\n            \"id\" : [\"A.2\", \"empty\", \"2\"]\n          }\n        }, \n        {\n          \"from\" : {\n            \"id\" : [\"A.2\", \"empty\", \"2\"]\n          }, \n          \"to\" : {\n            \"id\" : [\"A.2\", \"lace\", \"1\"]\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/examples/README.md",
    "content": "Examples\n--------\n\nThis folder contains the pattern that are useful to see how to specify the format.\n\nAlso, have a look at \n\n- [![](http://www.garnstudio.com/drops/mag/166/20/20-2.jpg)](http://www.garnstudio.com/pattern.php?id=7077&cid=9)\n  - bindoff\n  - knit plaited\n- [![](http://www.garnstudio.com/drops/mag/165/47/47-2.jpg)](http://www.garnstudio.com/pattern.php?id=7146&cid=9)\n  - cable knitting\n- [![](http://www.garnstudio.com/drops/mag/165/27/27-2.jpg)](http://www.garnstudio.com/pattern.php?id=7070&cid=9)\n  - circle knitting\n  - primitive lines\n  - cm specification\n- [![](http://www.garnstudio.com/drops/mag/167/11/11b-2.jpg)](http://www.garnstudio.com/pattern.php?id=7362&cid=9)\n  - complex and huge pattern\n- [![](http://www.garnstudio.com/drops/mag/169/18/18-diag2.jpg)](http://www.garnstudio.com/pattern.php?id=7467&cid=9)\n  - easy to sewing pattern"
  },
  {
    "path": "knittingpattern/examples/all-instructions.json",
    "content": "{\n  \"version\" : \"0.1\",\n  \"type\" : \"knitting pattern\", \n  \"comment\" : {\n    \"markdown\" : \"This pattern is taken from [garnstudio](http://www.garnstudio.com/pattern.php?id=7350&cid=19).\",\n    \"picture\" : {\n      \"type\" : \"url\",\n      \"url\" : \"http://www.garnstudio.com/drops/mag/167/17/17-lp.jpg\"\n    }\n  },\n  \"patterns\" : [\n    {\n      \"name\" : \"all-instructions\", \n      \"id\" : \"A.2\", \n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"color\" : \"#0000ff\",\n          \"instructions\" : [\n            {\"type\" : \"bo\"},\n            {\"type\" : \"cdd\"},\n            {\"type\" : \"co\"},\n            {\"type\" : \"default-unknown\"},\n            {\"type\" : \"k2tog\"},\n            {\"type\" : \"knit\"},\n            {\"type\" : \"purl\"},\n            {\"type\" : \"skp\"},\n            {\"type\" : \"yo\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/examples/block4x4.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"4x4 meshes block\",\n    \"type\" : \"markdown\"\n    },\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"knit 4x4\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"id\": \"1.0\", \"color\": \"green\"},\n            {\"id\": \"1.1\"},\n            {\"id\": \"1.2\"},\n            {\"id\": \"1.3\"}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"id\": \"3.0\"},\n            {\"id\": \"3.1\"},\n            {\"id\": \"3.2\", \"color\": \"green\"},\n            {\"id\": \"3.3\"}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"id\": \"2.0\"},\n            {\"id\": \"2.1\", \"color\": \"green\"},\n            {\"id\": \"2.2\"},\n            {\"id\": \"2.3\"}\n          ]\n        },\n        {\n          \"id\" : 4,\n          \"instructions\" : [\n            {\"id\": \"4.0\"},\n            {\"id\": \"4.1\"},\n            {\"id\": \"4.2\"},\n            {\"id\": \"4.3\", \"color\": \"green\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 1\n          },\n          \"to\" : {\n            \"id\" : 2\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          },\n          \"to\" : {\n            \"id\" : 3\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 3\n          },\n          \"to\" : {\n            \"id\" : 4\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/examples/empty.json",
    "content": "{\n  \"version\" : \"0.1\",\n  \"type\" : \"knitting pattern\", \n  \"patterns\" : [\n  ]\n}"
  },
  {
    "path": "knittingpattern/examples/negative-rendering.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"comment\" : {\n    \"content\" : \"rendering with negative layout indices\",\n    \"type\" : \"markdown\"\n    },\n  \"patterns\" : [\n    {\n      \"id\" : \"knit\",\n      \"name\" : \"knit 4x4\",\n      \"rows\" : [\n        {\n          \"id\" : 0,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{\"type\":\"yo\"}\n          ]\n        },\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{},{},{\"type\":\"yo\"}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{},{},{},{},{\"type\":\"yo\"}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{},{},{},{},{},{},{\"type\":\"yo\"}\n          ]\n        },\n        {\n          \"id\" : -1,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{},{},{\"type\":\"yo\"}\n          ]\n        },\n        {\n          \"id\" : -2,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{},{},{},{},{\"type\":\"yo\"}\n          ]\n        },\n        {\n          \"id\" : -3,\n          \"instructions\" : [\n            {\"type\":\"yo\"},{},{},{},{},{},{},{},{},{},{\"type\":\"yo\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 0\n          },\n          \"to\" : {\n            \"id\" : 1,\n            \"start\" : 1\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 1\n          },\n          \"to\" : {\n            \"id\" : 2,\n            \"start\" : 1\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          },\n          \"to\" : {\n            \"id\" : 3,\n            \"start\" : 1\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : -1\n          },\n          \"to\" : {\n            \"id\" : 0\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : -2\n          },\n          \"to\" : {\n            \"id\" : -1\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : -3\n          },\n          \"to\" : {\n            \"id\" : -2\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/examples/new-knitting-pattern.py",
    "content": "import json\n\ns = dict()\n\nright_mesh = dict()\n#    type of stitch\n#    k means a knit stitch and p means a purl stitch. Thus, \"k2, p2\",\n#      means \"knit two stitches, purl two stitches\". Similarly,\n#      sl st describes a slip stitch, whereas yarn-overs are denoted with yo.\nright_mesh[\"type\"] = \"knit\"  # purl\n#    scope of stitch\n#    The modifier tog indicates that the stitches should be knitted together,\n#      e.g., \"k2tog\" indicates that two stitches should be knitted together\n#      as though they were one stitch. psso means \"pass the slipped stitch\n#      over\". pnso means \"pass the next stitch over\".\nright_mesh[\"scope\"] = None\n#    orientation of stitch\n#    The modifier tbl indicates that stitches should be knitted\n#      through the back loop. For example, \"p2tog tbl\" indicates\n#      that two stitches should be purled together through the back toop.\n#      kwise and pwise connote \"knitwise\" and \"purlwise\", usually referring\n#      to a slip stitch.\nright_mesh[\"orientation\"] = 0  # degrees\n#    insertion point of stitch\n#    k-b and k1b mean \"knit into the row below\". Similarly, p-b and\n#      p1b mean \"purl into the row below\".\n#    p tbl; P1 tbl; or P1b: Purl through the back loop.\nright_mesh[\"insertion_point\"] = None\n\nMESHES_IN_ROW_1 = 5\nMESHES_IN_ROW_2 = 7\n\nrow1 = dict()\nrow1[\"meshes\"] = [right_mesh] * MESHES_IN_ROW_1\n#   side of work\n#   RS and WS signify the \"right side\" and \"wrong side\" of the work.\nrow1[\"side\"] = \"right\"  # wrong\nrow1[\"id\"] = \"row1\"\n\ns[\"type\"] = \"knitting pattern\"\ns[\"version\"] = \"0.1\"\ns[\"rows\"] = [row1]\ns[\"row_connections\"] = [{\"from\": {\"id\": \"row1\",\n                                  \"first\": 1,\n                                  \"last\": MESHES_IN_ROW_1},\n                         \"to\": {\"id\": \"row2\",\n                                \"first\": 1,\n                                \"last\": MESHES_IN_ROW_2}}]\n\nprint(json.dumps(s, indent=2))\n"
  },
  {
    "path": "knittingpattern/instructions/bo.json",
    "content": "[\n    {\n        \"type\" : \"bo\",\n        \"number of consumed meshes\" : 1,\n        \"number of produced meshes\" : 0,\n        \"title\" : {\n            \"en-en\" : \"Simple Bind Off\"\n        },\n        \"description\" : {\n            \"wikipedia\" : {\n                \"en-en\" : \"https://en.wikipedia.org/wiki/Binding_off\"\n            },\n            \"text\" : {\n                \"en-en\" : \"Pass each loop over an adjacent stitch. (The yarn is passed through the final loop to secure the whole chain.) This technique produces a tight edge with little elasticity.\"\n            }\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/cdd.json",
    "content": "\n[\n    {\n        \"type\" : \"cdd\",\n        \"number of consumed meshes\" : 3,\n        \"title\" : {\n            \"en-en\" : \"Center Double Decrease\"\n        },\n        \"description\" : {\n            \"text\" : {\n                \"en-en\" : \"Knit tree meshes together in the middle.\"\n            },\n            \"youtube\" : {\n                \"en-en\" : \"https://www.youtube.com/watch?v=Wi5JpkOoLCI\"\n            }\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/co.json",
    "content": "[\n    {\n        \"type\" : \"co\",\n        \"number of consumed meshes\" : 0,\n        \"number of produced meshes\" : 1,\n        \"grid-layout\" : {\n            \"width\" : 1\n        },\n        \"title\" : {\n            \"en-en\" : \"Single Cast On\"\n        },\n        \"description\" : {\n            \"wikipedia\" : {\n                \"en-en\" : \"https://en.wikipedia.org/wiki/Casting_on_%28knitting%29\"\n            },\n            \"text\" : {\n                \"en-en\" : \"An even simpler method, also called the simple cast-on or 'backward loop cast-on,' which involves adding a series of half hitches to the needle. This creates a very stretchy, flexible edge. It is a common approach for adding several stitches to the edge in the middle of a knitted fabric.\"\n            }\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/k2tog.json",
    "content": "\n[\n    {\n        \"type\" : \"k2tog\",\n        \"title\" : {\n            \"en-en\" : \"Knit 2 Together\"\n        },\n        \"number of consumed meshes\" : 2,\n        \"description\" : {\n            \"wikipedia\" : {\n                \"en-en\" : \"https://en.wikipedia.org/wiki/Knitting_abbreviations#Types_of_knitting_abbreviations\"\n            },\n            \"text\" : {\n                \"en-en\" : \"Knit two stitches together, as if they were one stitch.\"\n            }\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/knit.json",
    "content": "[\n    {\n        \"type\" : \"knit\",\n        \"title\" : {\n            \"en-en\" : \"Knit\",\n            \"de-de\" : \"Rechte Masche\"\n        },\n        \"description\" : {\n            \"text\" : {\n                \"en-en\" : \"Knit from the right side, purl from the wrong side.\"\n            }\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/purl.json",
    "content": "[\n    {\n        \"type\" : \"purl\",\n        \"title\" : {\n            \"en-en\" : \"Purl\",\n            \"de-de\" : \"Linke Masche\"\n        },\n        \"description\" : {\n            \"text\" : {\n                \"en-en\" : \"Purl from the right side, knit from the wrong side.\"\n            }\n        }\n\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/skp.json",
    "content": "\n[\n    {\n        \"type\" : \"skp\",\n        \"number of consumed meshes\" : 2,\n        \"title\" : {\n            \"en-en\" : \"Slip Knit Pass\"\n        },\n        \"description\" : {\n            \"wikipedia\" : {\n                \"en-en\" : \"https://en.wikipedia.org/wiki/Knitting_abbreviations#Types_of_knitting_abbreviations\"\n            },\n            \"text\" : {\n                \"en-en\" : \"Slip, knit, pass slipped stitch over the knit stitch (the same as sl1, k1, psso).\"\n            }\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/instructions/yo.json",
    "content": "\n[\n    {\n        \"type\" : \"yo\",\n        \"number of consumed meshes\" : 0,\n        \"title\" : {\n            \"en-en\" : \"Yarn Over\"\n        },\n        \"description\" : {\n            \"text\" : {\n                \"en-en\" : \"Increase the number of stitches by one.\"\n            },\n            \"wikipedia\" : {\n                \"en-en\" : \"https://en.wikipedia.org/wiki/Yarn_over\"\n            }\n        },\n        \"render\" : {\n            \"z\" : 1,\n            \"comment\" : \"Increase the z value to place the yarn over before other meshes.\"\n        }\n    },\n    {\n        \"type\" : \"yo twisted\",\n        \"title\" : {\n            \"en-en\" : \"Twisted Yarn Over\"\n        },\n        \"number of consumed meshes\" : 0,\n        \"description\" : {\n            \"text\" : {\n                \"en-en\" : \"Increase the number of stitches by one. Twist the yarn to avoid a hole.\"\n            },\n            \"wikipedia\" : {\n                \"en-en\" : \"https://en.wikipedia.org/wiki/Yarn_over\"\n            }\n        },\n        \"render\" : {\n            \"z\" : 1,\n            \"comment\" : \"Increase the z value to place the yarn over before other meshes.\"\n        }\n    }\n]"
  },
  {
    "path": "knittingpattern/test/conftest.py",
    "content": "\"\"\"This module holds the common test code.\n\n.. seealso:: `pytest good practices\n  <https://pytest.org/latest/goodpractices.html>`__ for why this module exists.\n\"\"\"\nimport os\nimport sys\n\n# sys.path makes knittingpattern importable\nHERE = os.path.dirname(__file__)\nsys.path.insert(0, os.path.join(HERE, \"../..\"))\n__builtins__[\"HERE\"] = HERE\n"
  },
  {
    "path": "knittingpattern/test/pattern/inheritance.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"patterns\" : [\n    {\n      \"id\" : \"color test\",\n      \"name\" : \"colored line\",\n      \"rows\" : [\n        {\n          \"id\" : \"colored\",\n          \"color\": \"blue\",\n          \"instructions\" : [\n            {\"color\": \"green\"},\n            {}\n          ]\n        },\n        {\n          \"id\" : \"inherited uncolored +instructions\",\n          \"same as\" : \"inherited uncolored\",\n          \"instructions\" : [\n            {},\n            {\"color\": \"brown\"}\n          ]\n        },\n        {\n          \"id\" : \"inherited colored +instructions\",\n          \"same as\" : \"inherited colored\",\n          \"instructions\" : [\n            {},\n            {\"color\": \"red\"}\n          ]\n        },\n        {\n          \"id\" : \"uncolored\",\n          \"instructions\" : [\n            {},\n            {\"color\": \"yellow\"}\n          ]\n        },\n        {\n          \"id\" : \"inherited uncolored\",\n          \"same as\" : \"uncolored\"\n        },\n        {\n          \"id\" : \"inherited colored\",\n          \"same as\" : \"colored\"\n        }\n      ],\n      \"connections\" : [\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/test/pattern/row_mapping_pattern.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"patterns\" : [\n    {\n      \"id\" : \"A.1\",\n      \"name\" : \"A.1\",\n      \"rows\" : [\n        {\n          \"id\" : \"1.1\",\n          \"instructions\" : [\n            {\"id\": \"1.1.0\"},\n            {\"id\": \"1.1.1\"},\n            {\"id\": \"1.1.2\", \"type\": \"yo\"},\n            {\"id\": \"1.1.3\"},\n            {\"id\": \"1.1.4\"}\n          ]\n        },\n        {\n          \"id\" : \"2.1\",\n          \"instructions\" : [\n            {\"id\": \"2.1.0\"},\n            {\"id\": \"2.1.1\"}\n          ]\n        },\n        {\n          \"id\" : \"2.2\",\n          \"instructions\" : [\n            {\"id\": \"2.2.0\"},\n            {\"id\": \"2.2.1\"}\n          ]\n        },\n        {\n          \"id\" : \"3.2\",\n          \"instructions\" : [\n            {\"id\": \"3.2.0\"},\n            {\"id\": \"3.2.1\"}\n          ]\n        },\n        {\n          \"id\" : \"4.1\",\n          \"instructions\" : [\n            {\"id\": \"4.1.0\"},\n            {\"id\": \"4.1.1\"},\n            {\"id\": \"4.1.2\", \"type\": \"skp\"},\n            {\"id\": \"4.1.4\"}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : \"1.1\",\n            \"start\" : 0\n          },\n          \"to\" : {\n            \"id\" : \"2.1\",\n            \"start\" : 0\n          },\n          \"meshes\" : 2\n        },\n        {\n          \"from\" : {\n            \"id\" : \"1.1\",\n            \"start\" : 3\n          },\n          \"to\" : {\n            \"id\" : \"2.2\",\n            \"start\" : 0\n          },\n          \"meshes\" : 2\n        },\n        {\n          \"from\" : {\n            \"id\" : \"2.2\",\n            \"start\" : 0\n          },\n          \"to\" : {\n            \"id\" : \"3.2\",\n            \"start\" : 0\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : \"2.1\",\n            \"start\" : 0\n          },\n          \"to\" : {\n            \"id\" : \"4.1\",\n            \"start\" : 0\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : \"3.2\",\n            \"start\" : 0\n          },\n          \"to\" : {\n            \"id\" : \"4.1\",\n            \"start\" : 3\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/test/pattern/row_removal_pattern.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"patterns\" : [\n    {\n      \"id\" : \"line\",\n      \"name\" : \"line of three meshes\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {\"id\": 1.1},\n            {\"id\": 1.2}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {\"id\": 2.1}\n          ]\n        },\n        {\n          \"id\" : 3,\n          \"instructions\" : [\n            {\"id\": 3.1}\n          ]\n        }\n      ],\n      \"connections\" : [\n        {\n          \"from\" : {\n            \"id\" : 1\n          },\n          \"to\" : {\n            \"id\" : 2\n          }\n        },\n        {\n          \"from\" : {\n            \"id\" : 2\n          },\n          \"to\" : {\n            \"id\" : 3\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/test/pattern/single_instruction.json",
    "content": "{\n  \"type\" : \"knitting pattern\",\n  \"version\" : \"0.1\",\n  \"patterns\" : [\n    {\n      \"id\" : \"A.1\",\n      \"name\" : \"A.1\",\n      \"rows\" : [\n        {\n          \"id\" : 1,\n          \"instructions\" : [\n            {}\n          ]\n        },\n        {\n          \"id\" : 2,\n          \"instructions\" : [\n            {}\n          ]\n        }\n      ],\n      \"connections\" : [\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "knittingpattern/test/test_add_and_remove_instructions.py",
    "content": "\"\"\"Test the maniipulation of the rows by adding instructions.\"\"\"\nfrom pytest import fixture, raises\nfrom knittingpattern import load_from_relative_file\nfrom knittingpattern.Instruction import InstructionNotFoundInRow\n\n\n@fixture\ndef single_instruction_pattern_set():\n    \"\"\"Load the pattern set with only one instruction.\"\"\"\n    return load_from_relative_file(HERE, \"pattern/single_instruction.json\")\n\n\n@fixture\ndef pattern(single_instruction_pattern_set):\n    \"\"\"The pattern which has only one instruction.\"\"\"\n    return single_instruction_pattern_set.patterns[\"A.1\"]\n\n\n@fixture\ndef row(pattern):\n    \"\"\"The row with one instruction.\"\"\"\n    return pattern.rows[1]\n\n\n@fixture\ndef row2(pattern):\n    \"\"\"The row with one instruction.\"\"\"\n    return pattern.rows[2]\n\n\n@fixture\ndef instruction(row):\n    \"\"\"The instruction.\"\"\"\n    return row.instructions[0]\n\n\n@fixture\ndef instruction2(row2):\n    \"\"\"The instruction.\"\"\"\n    return row2.instructions[0]\n\n\n@fixture\ndef empty_row(row, instruction):\n    \"\"\"Now, there is no instruction any more.\"\"\"\n    assert instruction\n    row.instructions.pop()\n    return row\n\n\ndef test_there_is_only_one_instruction(row):\n    \"\"\"There should be only one instruction, as claimed many times.\n\n    If people write that there is only one instruction, we should make that\n    sure!\"\"\"\n    assert len(row.instructions) == 1\n\n\ndef test_removing_the_instruction_gives_an_error_when_accessing_its_index(\n        empty_row, instruction):\n    \"\"\"Obviously a removed instruction is not in its row any more and thus has\n    no index.\"\"\"\n    with raises(InstructionNotFoundInRow):\n        instruction.index_in_row\n    assert not instruction.is_in_row()\n\n\ndef test_inserting_a_new_instruction_loads_its_config(row):\n    row.instructions.append({})\n    instruction = row.instructions[-1]\n    assert instruction.type == \"knit\"\n    assert instruction.is_in_row()\n    assert instruction.row == row\n    assert instruction.index_in_row == 1\n\n\ndef test_insert_an_existing_instruction(row, instruction2, row2):\n    row.instructions.insert(0, instruction2)\n    assert instruction2.row == row\n    assert instruction2.index_in_row == 0\n    assert row2.instructions == []\n\n\ndef test_transfer_removed_instruction(row, row2):\n    row2.instructions.append(row.instructions.pop())\n    instruction = row2.instructions[-1]\n    assert instruction.row == row2\n"
  },
  {
    "path": "knittingpattern/test/test_change_row_mapping.py",
    "content": "\"\"\"Test if the mapping of the rows is changed and how.\"\"\"\nfrom pytest import fixture, raises\nfrom knittingpattern import load_from_relative_file as load_relative_file\n\n\n@fixture\ndef patterns():\n    return load_relative_file(__file__, \"pattern/row_removal_pattern.json\")\n\n\n@fixture\ndef line(patterns):\n    return patterns.patterns[\"line\"]\n\n\n@fixture\ndef row1(line):\n    return line.rows[1]\n\n\n@fixture\ndef row2(line):\n    return line.rows[2]\n\n\n@fixture\ndef row3(line):\n    return line.rows[3]\n\n# produced meshes\n\n\n@fixture\ndef mesh11p(row1):\n    return row1.produced_meshes[0]\n\n\n@fixture\ndef mesh12p(row1):\n    return row1.produced_meshes[1]\n\n\n@fixture\ndef mesh21p(row2):\n    return row2.produced_meshes[0]\n\n\n@fixture\ndef mesh31p(row3):\n    return row3.produced_meshes[0]\n\n# consumed meshes\n\n\n@fixture\ndef mesh11c(row1):\n    return row1.consumed_meshes[0]\n\n\n@fixture\ndef mesh12c(row1):\n    return row1.consumed_meshes[1]\n\n\n@fixture\ndef mesh21c(row2):\n    return row2.consumed_meshes[0]\n\n\n@fixture\ndef mesh31c(row3):\n    return row3.consumed_meshes[0]\n\n\n@fixture\ndef connected_meshes(mesh11p, mesh21c, mesh21p, mesh31c):\n    return (mesh11p, mesh21c, mesh21p, mesh31c)\n\n\n@fixture\ndef disconnected_meshes(mesh11c, mesh12c, mesh12p, mesh31p):\n    return (mesh11c, mesh12c, mesh12p, mesh31p)\n\n\n@fixture\ndef meshes(connected_meshes, disconnected_meshes):\n    return connected_meshes + disconnected_meshes\n\n\n@fixture\ndef connections(mesh11p, mesh21c, mesh21p, mesh31c):\n    return ((mesh11p, mesh21c), (mesh21p, mesh31c))\n\n\n@fixture\ndef two_way_connections(mesh11p, mesh21c, mesh21p, mesh31c):\n    return ((mesh11p, mesh21c), (mesh21c, mesh11p), (mesh21p, mesh31c),\n            (mesh31c, mesh21p))\n\n\n@fixture\ndef produced_meshes(mesh11p, mesh12p, mesh21p, mesh31p):\n    return (mesh11p, mesh12p, mesh21p, mesh31p)\n\n\n@fixture\ndef consumed_meshes(mesh11c, mesh12c, mesh21c, mesh31c):\n    return (mesh11c, mesh12c, mesh21c, mesh31c)\n\n\ndef pytest_generate_tests(metafunc):\n    if \"connect\" in metafunc.fixturenames:\n        metafunc.parametrize(\"connect\", [0, 1])\n    if \"disconnect\" in metafunc.fixturenames:\n        metafunc.parametrize(\"disconnect\", [0, 1])\n\n\ndef connect_meshes(mesh1, mesh2, connect):\n    if connect == 1:\n        mesh2, mesh1 = mesh1, mesh2\n    mesh1.connect_to(mesh2)\n\n\ndef disconnect_meshes(mesh1, mesh2, disconnect):\n    mesh = (mesh1, mesh2)[disconnect]\n    mesh.disconnect()\n\n\nclass TestLine(object):\n\n    \"\"\"Make sure the pattern is what we expect.\"\"\"\n\n    def test_consumed_meshes_of_row1(self, row1, mesh11c, mesh12c):\n        for mesh in (mesh11c, mesh12c):\n            assert not mesh.is_produced()\n            assert mesh.is_consumed()\n            assert mesh.consuming_row == row1\n\n    def test_produced_meshes_of_row1(self, row1, mesh11p, mesh12p):\n        for mesh in (mesh11p, mesh12p):\n            assert mesh.is_produced()\n            assert mesh.producing_row == row1\n        assert mesh11p.is_consumed()\n        assert not mesh12p.is_consumed()\n\n    def test_consumed_mesh_of_row2(self, row2, mesh21c):\n        assert mesh21c.is_consumed()\n        assert mesh21c.is_produced()\n        assert mesh21c.consuming_row == row2\n\n    def test_produced_mesh_of_row2(self, row2, mesh21p):\n        assert mesh21p.is_consumed()\n        assert mesh21p.is_produced()\n        assert mesh21p.producing_row == row2\n\n    def test_consumed_mesh_of_row3(self, row3, mesh31c):\n        assert mesh31c.is_consumed()\n        assert mesh31c.is_produced()\n        assert mesh31c.consuming_row == row3\n\n    def test_produced_mesh_of_row3(self, row3, mesh31p):\n        assert not mesh31p.is_consumed()\n        assert mesh31p.is_produced()\n        assert mesh31p.producing_row == row3\n\n    def test_is_connected(self, connected_meshes):\n        for mesh in connected_meshes:\n            assert mesh.is_connected()\n\n    def test_is_disconnected(self, disconnected_meshes):\n        for mesh in disconnected_meshes:\n            assert not mesh.is_connected()\n            with raises(AssertionError):\n                mesh.as_consumed_mesh()\n                mesh.as_produced_mesh()\n\n    def test_equality(self, connections):\n        for produced_mesh, consumed_mesh in connections:\n            assert consumed_mesh.as_produced_mesh() == produced_mesh\n            assert produced_mesh.as_consumed_mesh() == consumed_mesh\n\n    def test_is_connected_to(self, two_way_connections):\n        for m1, m2 in two_way_connections:\n            assert m1.is_connected_to(m2)\n\n    def test_disconnected_from(self, connections, meshes):\n        \"\"\"Test all the meshes that are disconnected from each other.\"\"\"\n        for m1 in meshes:\n            assert m1 == m1\n            for m2 in meshes:\n                if m1 is m2:\n                    continue\n                assert m1 != m2\n                assert not m1 == m2\n                if (m1, m2) not in connections and (m2, m1) not in connections:\n                    assert not m1.is_connected_to(m2)\n                    if m1.is_connected() and m1 != m2:\n                        assert m1.as_produced_mesh() != m2 or m1 == m2\n                        assert m1.as_consumed_mesh() != m2 or m1 == m2\n\n    def test_as_produced_mesh(self, produced_meshes):\n        for produced_mesh in produced_meshes:\n            assert produced_mesh.as_produced_mesh() == produced_mesh\n\n    def test_as_consumed_mesh(self, consumed_meshes):\n        for consumed_mesh in consumed_meshes:\n            assert consumed_mesh.as_consumed_mesh() == consumed_mesh\n\n\ndef test_remove_a_connection(row1, row2, mesh11p, mesh21c, disconnect):\n    disconnect_meshes(mesh11p, mesh21c, disconnect)\n    assert mesh11p.is_produced()\n    assert not mesh11p.is_consumed()\n    assert mesh11p.producing_row == row1\n\n    assert not mesh21c.is_produced()\n    assert mesh21c.is_consumed()\n    assert mesh21c.consuming_row == row2\n\n    assert mesh11p != mesh21c\n    with raises(Exception):\n        mesh11p.as_consumed_mesh()\n    with raises(Exception):\n        mesh21c.as_produced_mesh()\n\n\ndef test_replace_a_connection(disconnect, connect, mesh21p, mesh31c, mesh12p,\n                              row1, row3):\n    \"\"\"Remove a connection and create one with a common mesh.\n\n    Remove a connection between mesh21p and mesh31c and create a connection\n    between mesh12p and mesh31c.\n    \"\"\"\n    disconnect_meshes(mesh21p, mesh31c, disconnect)\n    connect_meshes(mesh31c, mesh12p, connect)\n\n    assert not mesh21p.is_connected()\n    assert mesh31c.is_connected()\n    assert mesh12p.is_connected()\n\n    assert mesh31c.producing_row == row1\n    assert mesh12p.consuming_row == row3\n\n    assert mesh31c.as_produced_mesh() == mesh12p\n    assert mesh12p.as_consumed_mesh() == mesh31c\n\n\ndef test_connect_to_a_connected_location(mesh12p, mesh31c, mesh21p, connect):\n    connect_meshes(mesh12p, mesh31c, connect)\n    assert mesh12p.is_connected_to(mesh31c)\n    assert not mesh12p.is_connected_to(mesh21p)\n    assert not mesh31c.is_connected_to(mesh21p)\n    assert not mesh21p.is_connected()\n\n\ndef test_connect_to_a_connected_location_with_connected_mesh(\n        mesh11p, mesh31c, mesh21c, mesh21p, connect):\n    connect_meshes(mesh11p, mesh31c, connect)\n    assert mesh11p.is_connected_to(mesh31c)\n    assert not mesh21c.is_connected()\n    assert not mesh21p.is_connected()\n\n\ndef test_can_connect(connected_meshes, consumed_meshes, produced_meshes):\n    for consumed_mesh in consumed_meshes:\n        for produced_mesh in produced_meshes:\n            can_connect = consumed_mesh not in connected_meshes and \\\n                produced_mesh not in connected_meshes\n            assert produced_mesh.can_connect_to(consumed_mesh) == can_connect\n\n\ndef test_create_new_connection(mesh31p, mesh12c, connect, row1, row3):\n    connect_meshes(mesh31p, mesh12c, connect)\n\n    assert mesh31p.is_connected()\n    assert mesh12c.is_connected()\n\n    assert mesh12c.producing_row == row3\n    assert mesh31p.consuming_row == row1\n\n    assert mesh12c.as_produced_mesh() == mesh31p\n    assert mesh31p.as_consumed_mesh() == mesh12c\n\n\ndef test_disconnect_disconnected(mesh12c):\n    mesh12c.disconnect()\n    assert not mesh12c.is_connected()\n"
  },
  {
    "path": "knittingpattern/test/test_default_instructions.py",
    "content": "from pytest import fixture\nimport pytest\nfrom knittingpattern.InstructionLibrary import DefaultInstructions, \\\n    default_instructions\n\n\nDEFAULT_INSTRUCTIONS = {\n    \"knit\": (1, 1),\n    \"purl\": (1, 1),\n    \"skp\": (2, 1),\n    \"yo\": (0, 1),\n    \"yo twisted\": (0, 1),\n    \"k2tog\": (2, 1),\n    \"bo\": (1, 0),\n    \"cdd\": (3, 1),\n    \"co\": (0, 1)\n}\n\n\n@fixture\ndef default():\n    return DefaultInstructions()\n\n\n@pytest.mark.parametrize(\"type_,value\", DEFAULT_INSTRUCTIONS.items())\ndef test_mesh_consumption(default, type_, value):\n    assert default[type_].number_of_consumed_meshes == value[0]\n\n\n@pytest.mark.parametrize(\"type_,value\", DEFAULT_INSTRUCTIONS.items())\ndef test_mesh_production(default, type_, value):\n    assert default[type_].number_of_produced_meshes == value[1]\n\n\n@pytest.mark.parametrize(\"type_\", DEFAULT_INSTRUCTIONS.keys())\ndef test_description_present(default, type_):\n    assert default[type_].description\n\nUNTEDTED_MESSAGE = \"No default instructions shall be untested.\"\n\n\ndef test_all_default_instructions_are_tested(default):\n    untested_instructions = \\\n        set(default.loaded_types) - set(DEFAULT_INSTRUCTIONS)\n    assert not untested_instructions, UNTEDTED_MESSAGE\n\n\ndef test_default_instructions_is_a_singleton():\n    assert default_instructions() is default_instructions()\n\n\ndef test_default_instructions_are_an_instance_of_the_class():\n    assert isinstance(default_instructions(), DefaultInstructions)\n"
  },
  {
    "path": "knittingpattern/test/test_dump_json.py",
    "content": "from pytest import fixture\nfrom unittest.mock import MagicMock\nfrom knittingpattern.Dumper import JSONDumper\nimport json\nfrom knittingpattern.ParsingSpecification import ParsingSpecification\n\n\n@fixture\ndef obj():\n    return [\"123\", 123]\n\n\n@fixture\ndef dumper(obj):\n    def dump():\n        return obj\n    return JSONDumper(dump)\n\n\n@fixture\ndef parser():\n    return MagicMock()\n\n\ndef test_dump_object(dumper, obj):\n    assert dumper.object() == obj\n\n\ndef test_dump_string(dumper, obj):\n    assert dumper.string() == json.dumps(obj)\n\n\ndef test_dump_to_temporary_file(dumper, obj):\n    temp_path = dumper.temporary_path()\n    with open(temp_path) as file:\n        obj2 = json.load(file)\n    assert obj2 == obj\n\n\ndef test_dump_to_knitting_pattern(dumper, parser, obj):\n    spec = ParsingSpecification(new_parser=parser)\n    dumper.knitting_pattern(spec)\n    parser.assert_called_with(spec)\n    parser(spec).knitting_pattern_set.assert_called_with(obj)\n\n\ndef test_string_representation(dumper):\n    string = repr(dumper)\n    assert \"JSONDumper\" in string\n"
  },
  {
    "path": "knittingpattern/test/test_dumper.py",
    "content": "from pytest import fixture\nfrom knittingpattern.Dumper import ContentDumper\nfrom io import StringIO, BytesIO\nimport os\n\n\nSTRING = \"asdf1234567890\\u1234\"\nBYTES = STRING.encode(\"UTF-8\")\n\n\n@fixture\ndef unicode():\n    def dump_to_string(file):\n        file.write(STRING[:1])\n        file.write(STRING[1:])\n    return ContentDumper(dump_to_string)\n\n\n@fixture\ndef binary():\n    def dump_to_bytes(file):\n        file.write(BYTES[:1])\n        file.write(BYTES[1:])\n    return ContentDumper(dump_to_bytes, text_is_expected=False)\n\n\n@fixture\ndef no_encode_text():\n    return ContentDumper(lambda file: file.write(\"asd\"), encoding=None)\n\n\n@fixture\ndef no_encode_binary():\n    return ContentDumper(lambda file: file.write(b\"asd\"),\n                         text_is_expected=False,\n                         encoding=None)\n\n\ndef pytest_generate_tests(metafunc):\n    if 'save_to' in metafunc.fixturenames:\n        metafunc.parametrize(\"save_to\", [binary(), unicode()])\n\n\n@fixture\ndef temp_file(save_to):\n    return save_to.temporary_file()\n\n\n@fixture\ndef binary_temp_file(save_to):\n    return save_to.temporary_binary_file()\n\n\n@fixture\ndef stringio():\n    return StringIO()\n\n\ndef assert_string_is_file_content(file):\n    file.seek(0)\n    assert file.read() == STRING\n\n\ndef assert_string_is_path_content(path):\n    with open(path, encoding=\"UTF-8\") as file:\n        assert file.read() == STRING\n\n\ndef assert_string_is_binary_content(file):\n    file.seek(0)\n    assert file.read() == BYTES\n\n\ndef test_string_is_long():\n    assert len(STRING) > 5\n\n\ndef test_dump_to_string(save_to):\n    assert save_to.string() == STRING\n\n\ndef test_dump_to_file(save_to, stringio):\n    save_to.file(stringio)\n    assert_string_is_file_content(stringio)\n\n\ndef test_dump_is_behind_content_in_file(save_to, stringio):\n    save_to.file(stringio)\n    assert stringio.read() == \"\"\n\n\ndef test_dump_to_path(save_to, tmpdir):\n    path = tmpdir.mkdir(\"sub\").join(\"temp.txt\").strpath\n    save_to.path(path)\n    assert_string_is_path_content(path)\n\n\ndef test_dump_to_temp_path(save_to):\n    path = save_to.temporary_path()\n    assert_string_is_path_content(path)\n\n\ndef test_dump_to_temporary_file(temp_file):\n    assert_string_is_file_content(temp_file)\n\n\ndef test_dump_to_temporary_binary_file(binary_temp_file):\n    assert_string_is_binary_content(binary_temp_file)\n\n\ndef test_temporary_file_is_deleted_on_default(temp_file):\n    assert_temporary_file_is_deleted(temp_file)\n\n\ndef test_binary_temporary_file_is_deleted_on_default(binary_temp_file):\n    assert_temporary_file_is_deleted(binary_temp_file)\n\n\ndef assert_temporary_file_is_deleted(temp_file):\n    temp_file.close()\n    assert not os.path.isfile(temp_file.name)\n\n\ndef assert_temporary_file_is_not_deleted(temp_file):\n    temp_file.close()\n    assert os.path.isfile(temp_file.name)\n\n\ndef test_temporary_file_exists(temp_file):\n    assert os.path.isfile(temp_file.name)\n\n\ndef test_binary_temporary_file_exists(binary_temp_file):\n    assert os.path.isfile(binary_temp_file.name)\n\n\ndef test_temporary_file_has_option_for_deletion(save_to):\n    file = save_to.temporary_file(delete_when_closed=False)\n    assert_temporary_file_is_not_deleted(file)\n\n\ndef test_binary_temporary_file_has_option_for_deletion(save_to):\n    file = save_to.binary_temporary_file(delete_when_closed=False)\n    assert_temporary_file_is_not_deleted(file)\n\n\ndef test_file_returns_new_file(save_to):\n    file = save_to.file()\n    assert_string_is_file_content(file)\n\n\ndef test_dump_is_behind_content_in_new_file(save_to, stringio):\n    file = save_to.file()\n    assert file.read() == \"\"\n\n\ndef test_bytes(save_to):\n    assert save_to.bytes() == BYTES\n\n\ndef test_encoding(save_to):\n    assert save_to.encoding == \"UTF-8\"\n\n\ndef test_new_binary_file(save_to):\n    file = save_to.binary_file()\n    file.seek(0)\n    assert file.read() == BYTES\n\n\ndef test_binary_file(save_to):\n    file = BytesIO()\n    save_to.binary_file(file)\n    file.seek(0)\n    assert file.read() == BYTES\n\n\ndef test_test_binary_file_is_at_end(save_to):\n    assert not save_to.binary_file().read()\n\n\ndef test_encoding_is_none(no_encode_binary, no_encode_text):\n    assert no_encode_text.encoding is None\n    assert no_encode_binary.encoding is None\n\n\ndef test_temporary_path_has_extension(save_to):\n    assert save_to.temporary_path(extension=\".png\").endswith(\".png\")\n    assert save_to.temporary_path(extension=\".JPG\").endswith(\".JPG\")\n\n\ndef test_string_representation(save_to):\n    string = repr(save_to)\n    assert string.startswith(\"<ContentDumper\")\n    assert string.endswith(\">\")\n    assert save_to.encoding in string\n"
  },
  {
    "path": "knittingpattern/test/test_example_code.py",
    "content": "\"\"\"The files contain example code that should work.\"\"\"\n\n\ndef test_load_from_example_and_create_svg():\n    \"\"\"Test :meth:`knittingpattern.load_from`.\"\"\"\n    import knittingpattern\n    k = knittingpattern.load_from().example(\"Cafe.json\")\n    k.to_svg(25).temporary_path(\".svg\")\n"
  },
  {
    "path": "knittingpattern/test/test_example_rows.py",
    "content": "\"\"\"Test properties of rows.\"\"\"\nfrom pytest import fixture, raises\nfrom test_examples import charlotte as _charlotte\n\n\n@fixture\ndef charlotte():\n    return _charlotte()\n\n\n@fixture\ndef a1(charlotte):\n    \"\"\":return: the pattern ``\"A.1\"`` in charlotte\"\"\"\n    return charlotte.patterns[\"A.1\"]\n\n\n@fixture\ndef a2(charlotte):\n    \"\"\":return: the pattern ``\"A.2\"`` in charlotte\"\"\"\n    return charlotte.patterns[\"A.2\"]\n\n\ndef test_number_of_rows(a1):\n    \"\"\"``\"A.1\"`` should have three rows that can be accessed\"\"\"\n    assert len(a1.rows) == 3\n    with raises(IndexError):\n        a1.rows.at(3)\n\n\ndef test_row_ids(a1):\n    \"\"\"Rows in ``\"A.1\"`` have ids.\"\"\"\n    assert a1.rows.at(0).id == (\"A.1\", \"empty\", \"1\")\n    assert a1.rows.at(2).id == (\"A.1\", \"lace\", \"1\")\n\n\ndef test_access_by_row_ids(a1):\n    \"\"\"Rows in ``\"A.1\"`` can be accessed by their ids.\"\"\"\n    assert a1.rows[(\"A.1\", \"empty\", \"1\")] == a1.rows.at(0)\n\n\ndef test_iterate_on_rows(a1):\n    \"\"\"For convinience one can iterate over the rows.\"\"\"\n    assert list(iter(a1.rows)) == [a1.rows.at(0), a1.rows.at(1), a1.rows.at(2)]\n"
  },
  {
    "path": "knittingpattern/test/test_examples.py",
    "content": "from pytest import fixture, raises\nimport os\nimport knittingpattern\n\nEXAMPLES_PATH = os.path.join(os.path.dirname(__file__), \"../examples\")\nCAFE_PATH = os.path.join(EXAMPLES_PATH, \"Cafe.json\")\nCHARLOTTE_PATH = os.path.join(EXAMPLES_PATH, \"Charlotte.json\")\nCAFE_STRING = open(CAFE_PATH).read()\nCHARLOTTE_STRING = open(CHARLOTTE_PATH).read()\n\n\n@fixture\ndef charlotte():\n    return knittingpattern.load_from_string(CHARLOTTE_STRING)\n\n\n@fixture\ndef cafe():\n    return knittingpattern.load_from_string(CAFE_STRING)\n\n\ndef test_number_of_patterns(charlotte):\n    assert len(charlotte.patterns) == 2\n    with raises(IndexError):\n        charlotte.patterns.at(3)\n\n\n@fixture\ndef pattern_0(charlotte):\n    return charlotte.patterns.at(0)\n\n\n@fixture\ndef pattern_1(charlotte):\n    return charlotte.patterns.at(1)\n\n\ndef test_names(pattern_0, pattern_1):\n    assert pattern_0.name == \"A.1\"\n    assert pattern_1.name == \"A.2\"\n\n\ndef test_ids(pattern_0, pattern_1):\n    assert pattern_0.id == \"A.1\"\n    assert pattern_1.id == \"A.2\"\n\n\ndef test_access_with_id(charlotte):\n    assert charlotte.patterns[\"A.1\"] == charlotte.patterns.at(0)\n\n\ndef test_iterate_on_pattern(charlotte):\n    patterns = charlotte.patterns\n    assert list(iter(patterns)) == [patterns.at(0), patterns.at(1)]\n"
  },
  {
    "path": "knittingpattern/test/test_id_collection.py",
    "content": "from pytest import fixture, raises\nfrom knittingpattern.IdCollection import IdCollection\nfrom collections import namedtuple\n\n\nI = namedtuple(\"Item\", [\"id\"])\n\n\n@fixture\ndef c():\n    return IdCollection()\n\n\ndef test_no_object(c):\n    assert not c\n    assert not list(c)\n\n\ndef test_add_object(c):\n    c.append(I(\"123\"))\n    c.append(I(\"122\"))\n    assert c.at(0).id == \"123\"\n    assert c.at(1).id == \"122\"\n    assert c[\"123\"].id == \"123\"\n    assert c[\"122\"].id == \"122\"\n\n\ndef test_length(c):\n    assert len(c) == 0\n    c.append(I(1))\n    assert len(c) == 1\n    c.append(I(\"\"))\n    assert len(c) == 2\n\n\ndef test_at_raises_keyerror(c):\n    with raises(KeyError):\n        c[\"unknown-id\"]\n"
  },
  {
    "path": "knittingpattern/test/test_instruction.py",
    "content": "from pytest import fixture\nfrom knittingpattern.Instruction import Instruction\nimport pytest\n\n\n@fixture\ndef default_instruction():\n    return Instruction({})\n\n\n@fixture\ndef purl():\n    return Instruction({\"type\": \"purl\"})\n\n\n@fixture\ndef yo():\n    return Instruction({\"type\": \"yo\", \"number of consumed meshes\": 0})\n\n\n@fixture\ndef bindoff():\n    return Instruction({\"type\": \"bindoff\", \"number of produced meshes\": 0})\n\n\n@fixture\ndef colored_instruction():\n    return Instruction({\"type\": \"purl\",\n                        \"color\": \"blue\",\n                        \"custom name\": \"custom value\",\n                        \"not inherited value\": 1},\n                       [{\"color\": \"green\",\n                         \"inherited value\": 0,\n                         \"not inherited value\": 2},\n                        {\"other inherited value\": 4},\n                        {\"other inherited value\": 0}])\n\n\ndef test_default_type(default_instruction):\n    assert default_instruction.type == \"knit\"\n    assert default_instruction.does_knit()\n    assert not default_instruction.does_purl()\n\n\ndef test_default_color(default_instruction):\n    assert not default_instruction.has_color()\n    assert default_instruction.color is None\n\n\ndef test_width(default_instruction, purl):\n    assert default_instruction.number_of_consumed_meshes == 1\n    assert default_instruction.number_of_produced_meshes == 1\n    assert purl.number_of_consumed_meshes == 1\n    assert purl.number_of_produced_meshes == 1\n\n\ndef test_purl_is_not_knit(purl):\n    assert not purl.does_knit()\n    assert purl.does_purl()\n\n\ndef test_color(colored_instruction):\n    assert colored_instruction.color == \"blue\"\n    assert \"custom name\" in colored_instruction\n    assert colored_instruction[\"custom name\"] == \"custom value\"\n\n\ndef test_inheritance(colored_instruction):\n    assert colored_instruction[\"not inherited value\"] == 1\n    assert colored_instruction[\"inherited value\"] == 0\n    assert colored_instruction[\"other inherited value\"] == 4\n\n\ndef test_purl_produces_meshes(purl):\n    assert purl.produces_meshes()\n\n\ndef test_purl_consumes_meshes(purl):\n    assert purl.consumes_meshes()\n\n\ndef test_yarn_over_consumes_no_meshes(yo):\n    assert yo.number_of_consumed_meshes == 0\n    assert not yo.consumes_meshes()\n\n\ndef test_yarn_over_produces_meshes(yo):\n    assert yo.number_of_produced_meshes == 1\n    assert yo.produces_meshes()\n\n\ndef test_bindoff_consumes_meshes(bindoff):\n    assert bindoff.number_of_consumed_meshes == 1\n    assert bindoff.consumes_meshes()\n\n\ndef test_bindoff_produces_no_meshes(bindoff):\n    assert bindoff.number_of_produced_meshes == 0\n    assert not bindoff.produces_meshes()\n\n\nclass TestInstructionColors(object):\n\n    \"\"\"Test the Instruction.colors attribute.\"\"\"\n\n    @pytest.mark.parametrize(\"spec,colors\", [\n        ({}, [None]), ({\"color\": \"blue\"}, [\"blue\"]), ({\"color\": 123}, [123])])\n    def test_get_colors_from_color_specification(self, spec, colors):\n        instruction = Instruction(spec)\n        assert instruction.colors == colors\n"
  },
  {
    "path": "knittingpattern/test/test_instruction_library.py",
    "content": "from pytest import fixture\nfrom knittingpattern.InstructionLibrary import InstructionLibrary\n\nDESCRIPTION = \"here you can see how to knit: URL\"\nDESCRIPTION_2 = \"well, this is kinda a different description\"\n\nlibrary_instructions = [\n    {\n        \"type\": \"knit\",\n        \"description\": DESCRIPTION\n    },\n    {\n        \"type\": \"purl\",\n        \"inverse\": \"knit\"\n    },\n    {\n        \"type\": \"extravagant knit\",\n        \"color\": \"green\",\n        \"specialattribute\": True\n    }\n]\n\n# TODO: What happens if an instruction type is defined multiple times? Error?\n\n\n@fixture\ndef library():\n    return InstructionLibrary().load.object(library_instructions)\n\n\n@fixture\ndef library2(library):\n    spec = [\n        {\"type\": \"added\", \"a\": 1},\n        {\"type\": \"knit\", \"description\": DESCRIPTION_2}\n    ]\n    library.load.object(spec)\n    return library\n\n\n@fixture\ndef knit(library):\n    return library.as_instruction({\"type\": \"knit\"})\n\n\n@fixture\ndef purl(library):\n    return library.as_instruction({\"type\": \"purl\", \"color\": \"white\"})\n\n\n@fixture\ndef custom_knit(library):\n    return library.as_instruction({\"type\": \"extravagant knit\"})\n\n\ndef test_knit_type_attributes(knit):\n    assert knit.type == \"knit\"\n    assert knit[\"description\"] == DESCRIPTION\n    assert knit[\"type\"] == knit.type\n\n\ndef test_knit_has_no_color(knit):\n    assert \"color\" not in knit\n    assert \"type\" in knit\n\n\ndef test_purl_has_color(purl):\n    assert purl.color == \"white\"\n    assert \"color\" in purl\n\n\ndef test_not_everyting_is_known_by_purl(purl):\n    assert \"asd\" not in purl\n    assert \"inverse\" in purl\n    assert purl[\"inverse\"] == \"knit\"\n\n\ndef test_custom_type(custom_knit):\n    assert custom_knit[\"specialattribute\"]\n\n\ndef test_default_instruction_is_knit(library):\n    assert library.as_instruction({})[\"type\"] == \"knit\"\n\n\ndef test_library_does_not_forget_old_values(library2):\n    assert library2.as_instruction({\"knit\"})\n\n\ndef test_library_can_load_multiple_times(library2):\n    assert library2.as_instruction({\"type\": \"added\"})[\"a\"] == 1\n\n\ndef test_library_handles_loading_several_instructions_with_same_type(library2):\n    assert library2.as_instruction({})[\"description\"] == DESCRIPTION_2\n\n\ndef test_access_via_type(library):\n    assert library[\"knit\"][\"type\"] == \"knit\"\n\nUNLOADED = \"unloaded type\"\n\n\ndef test_when_library_load_instruction_it_is_in_its_types(library2):\n    library2.add_instruction({\"type\": UNLOADED})\n    assert UNLOADED in library2.loaded_types\n\n\ndef test_unloaded_instruction_is_not_in_the_types(library2):\n    assert UNLOADED not in library2.loaded_types\n"
  },
  {
    "path": "knittingpattern/test/test_instruction_row_inheritance.py",
    "content": "\"\"\"Test that the color attribute is inherited properly.\"\"\"\nfrom pytest import fixture\nimport pytest\nfrom knittingpattern import load_from_relative_file\n\n\n@fixture(scope=\"module\")\ndef coloring_pattern():\n    \"\"\"The pattern with one colored line and a uncolored line.\"\"\"\n    patterns = load_from_relative_file(__name__, \"pattern/inheritance.json\")\n    return patterns.patterns[\"color test\"]\n\nINSTRUCTION_INHERITANCE = [\n    (\"uncolored\", 0, None),      # Neither row nor instruction have a color.\n    (\"uncolored\", 1, \"yellow\"),  # Instruction uses own color.\n    (\"colored\", 0, \"green\"),      # Row color is used, instruction has none.\n    (\"colored\", 1, \"blue\"),     # Instruction prefers own color before row's.\n    (\"inherited uncolored\", 0, None),\n    (\"inherited uncolored\", 1, \"yellow\"),\n    (\"inherited colored\", 0, \"green\"),\n    (\"inherited colored\", 1, \"blue\"),\n    (\"inherited uncolored +instructions\", 0, None),\n    (\"inherited uncolored +instructions\", 1, \"brown\"),\n    (\"inherited colored +instructions\", 0, \"blue\"),\n    (\"inherited colored +instructions\", 1, \"red\")]\n\n\n@pytest.mark.parametrize(\"row_id,instuction_index,color\",\n                         INSTRUCTION_INHERITANCE)\ndef test_instruction_has_color(coloring_pattern, row_id,\n                               instuction_index, color):\n    \"\"\"Test that the instructions correctly inherit from their row.\"\"\"\n    row = coloring_pattern.rows[row_id]\n    instruction = row.instructions[instuction_index]\n    assert instruction.color == color\n\nROW_INHERITANCE = [\n    (\"colored\", \"blue\"),\n    (\"uncolored\", None),\n    (\"inherited colored\", \"blue\"),\n    (\"inherited uncolored\", None),\n    (\"inherited colored +instructions\", \"blue\"),\n    (\"inherited uncolored +instructions\", None)]\n\n\n@pytest.mark.parametrize(\"row_id,color\", ROW_INHERITANCE)\ndef test_rows_have_color(coloring_pattern, row_id, color):\n    \"\"\"Test that the rows correctly inherit or define their color.\"\"\"\n    row = coloring_pattern.rows[row_id]\n    assert row.color == color\n"
  },
  {
    "path": "knittingpattern/test/test_instructions/recursion/test_instruction_3.json",
    "content": "\n[\n    {\n        \"type\" : \"test3\",\n        \"value\" : 3\n    }\n]"
  },
  {
    "path": "knittingpattern/test/test_instructions/recursion/test_instruction_4.json",
    "content": "\n[\n    {\n        \"type\" : \"test4\",\n        \"value\" : 4\n    }\n]"
  },
  {
    "path": "knittingpattern/test/test_instructions/test_instruction_1.json",
    "content": "\n[\n    {\n        \"type\" : \"test1\",\n        \"value\" : 1\n    }\n]"
  },
  {
    "path": "knittingpattern/test/test_instructions/test_instruction_2.json",
    "content": "\n[\n    {\n        \"type\" : \"test2\",\n        \"value\" : 2\n    }\n]"
  },
  {
    "path": "knittingpattern/test/test_knittingpattern.py",
    "content": "from knittingpattern import new_knitting_pattern\nimport knittingpattern.KnittingPattern as KnittingPatternModule\nfrom knittingpattern.KnittingPattern import KnittingPattern\nfrom unittest.mock import Mock\nfrom test_row_instructions import a1\nimport knittingpattern\nfrom pytest import fixture\n\n\nclass TestInstructionColors(object):\n\n    \"\"\"Test KnittingPattern.instruction_colors.\"\"\"\n\n    @fixture\n    def unique(self, monkeypatch):\n        mock = Mock()\n        monkeypatch.setattr(KnittingPatternModule, \"unique\", mock)\n        return mock\n\n    @fixture\n    def rows_in_knit_order(self, rows, monkeypatch):\n        mock = Mock()\n        monkeypatch.setattr(KnittingPattern, \"rows_in_knit_order\", mock)\n        mock.return_value = rows\n        return mock\n\n    @fixture\n    def rows(self):\n        return [Mock(), Mock(), Mock()]\n\n    @fixture\n    def knittingpattern(self, rows):\n        return KnittingPattern(Mock(), Mock(), Mock(), Mock())\n\n    def test_result(self, knittingpattern, unique, rows_in_knit_order):\n        assert knittingpattern.instruction_colors == unique.return_value\n\n    def test_call_arguments(self, knittingpattern, unique, rows,\n                            rows_in_knit_order):\n        knittingpattern.instruction_colors\n        instruction_colors = [row.instruction_colors for row in rows]\n        unique.assert_called_once_with(instruction_colors)\n\n    def test_chalotte(self, a1):\n        assert a1.instruction_colors == [None]\n\n    def test_cafe(self):\n        pattern = knittingpattern.load_from().example(\"Cafe.json\").first\n        colors = [\"mocha latte\", \"dark brown\", \"brown\", \"white\", ]\n        assert pattern.instruction_colors == colors\n"
  },
  {
    "path": "knittingpattern/test/test_load_instructions.py",
    "content": "from pytest import fixture\nimport os\nfrom knittingpattern.InstructionLibrary import InstructionLibrary\n\n\n@fixture\ndef lib():\n    return InstructionLibrary()\n\n\ndef test_load_from_relative_file(lib):\n    relative_path = \"test_instructions/test_instruction_1.json\"\n    lib.load.relative_file(__file__, relative_path)\n    assert lib.as_instruction({\"type\": \"test1\"})[\"value\"] == 1\n    assert \"value\" not in lib.as_instruction({\"type\": \"test2\"})\n\n\ndef test_load_from_relative_folder(lib):\n    lib.load.relative_folder(__file__, \"test_instructions\")\n    assert lib.as_instruction({\"type\": \"test1\"})[\"value\"] == 1\n    assert lib.as_instruction({\"type\": \"test2\"})[\"value\"] == 2\n\n\ndef test_load_from_folder(lib):\n    folder = os.path.join(os.path.dirname(__file__), \"test_instructions\")\n    lib.load.folder(folder)\n    assert lib.as_instruction({\"type\": \"test2\"})[\"value\"] == 2\n    assert lib.as_instruction({\"type\": \"test1\"})[\"value\"] == 1\n\n\ndef test_loading_from_folder_recursively(lib):\n    lib.load.relative_folder(__file__, \"test_instructions\")\n    assert lib.as_instruction({\"type\": \"test3\"})[\"value\"] == 3\n"
  },
  {
    "path": "knittingpattern/test/test_loader.py",
    "content": "from pytest import fixture\nimport os\nimport pytest\nfrom knittingpattern.Loader import ContentLoader, JSONLoader, PathLoader\n\nEXAMPLES_DIRECTORY = os.path.join(HERE, \"..\", \"examples\")\n\n\n@fixture\ndef result():\n    return []\n\n\n@fixture\ndef loader(result):\n\n    def process(obj):\n        result.append(obj)\n        return len(result)\n\n    def chooses_path(path):\n        return \"_2\" in os.path.basename(path)\n\n    return ContentLoader(process, chooses_path)\n\n\n@fixture\ndef path_loader():\n    return PathLoader(lambda path: path)\n\n\n@fixture\ndef jsonloader(result):\n    return JSONLoader(result.append)\n\n\ndef test_loading_object_does_nothing(loader, result):\n    obj = []\n    loader.string(obj)\n    assert result[0] is obj\n\n\ndef test_processing_result_is_returned(loader):\n    assert loader.string(None) == 1\n    assert loader.string(None) == 2\n\n\ndef test_json_loader_loads_json(jsonloader, result):\n    jsonloader.string(\"{\\\"x\\\": 1}\")\n    assert result == [{\"x\": 1}]\n\n\ndef test_loader_would_like_to_load_path(loader):\n    assert loader.chooses_path(\"x_2.asd\")\n\n\ndef test_loader_does_not_like_certain_paths(loader):\n    assert not loader.chooses_path(\"x_1.asd\")\n\n\ndef test_loader_can_select_paths_it_likes(loader):\n    assert loader.choose_paths([\"_1\", \"_2\", \"_3\"]) == [\"_2\"]\n    assert loader.choose_paths([\"_123\", \"3_2\", \"4_2.as\"]) == [\"3_2\", \"4_2.as\"]\n\n\ndef test_loading_from_directory_selects_paths(loader):\n    paths_to_load = []\n    loader.path = lambda path: paths_to_load.append(path)\n    assert loader.relative_folder(__name__, \"test_instructions\")\n    assert len(paths_to_load) == 1\n    assert paths_to_load[0].endswith(\"test_instruction_2.json\")\n\n\ndef example_path(example):\n    return os.path.abspath(os.path.join(EXAMPLES_DIRECTORY, example))\n\n\n@pytest.mark.parametrize(\"example\", os.listdir(EXAMPLES_DIRECTORY))\ndef test_load_example(path_loader, example):\n    expected_path = example_path(example)\n    generated_path = os.path.abspath(path_loader.example(example))\n    assert generated_path == expected_path\n\n\ndef test_load_examples(path_loader):\n    example_paths = set()\n    for root, _, examples in os.walk(EXAMPLES_DIRECTORY):\n        for example in examples:\n            example_paths.add(os.path.abspath(os.path.join(root, example)))\n    generated_paths = list(map(os.path.abspath, path_loader.examples()))\n    assert set(generated_paths) == example_paths\n"
  },
  {
    "path": "knittingpattern/test/test_parsing.py",
    "content": "from pytest import fixture, raises\nimport knittingpattern\nimport json\n\nEMPTY_PATTERN = {\n    \"version\": \"0.1\",\n    \"type\": \"knitting pattern\"\n}\n\n\n@fixture\ndef temp_empty_pattern_path(tmpdir):\n    p = tmpdir.mkdir(\"sub\").join(\"empty_pattern.knit\")\n    with open(p.strpath, \"w\") as f:\n        json.dump(EMPTY_PATTERN, f)\n    return p.strpath\n\n\ndef assert_is_pattern(pattern):\n    assert pattern.type == \"knitting pattern\"\n    assert pattern.version == \"0.1\"\n\n\ndef test_can_import_empty_pattern_from_object():\n    pattern = knittingpattern.load_from_object(EMPTY_PATTERN)\n    assert_is_pattern(pattern)\n\n\ndef test_can_import_empty_pattern_from_string():\n    json_string = json.dumps(EMPTY_PATTERN)\n    pattern = knittingpattern.load_from_string(json_string)\n    assert_is_pattern(pattern)\n\n\ndef test_can_import_empty_pattern_from_file_object(temp_empty_pattern_path):\n    with open(temp_empty_pattern_path) as file:\n        pattern = knittingpattern.load_from_file(file)\n    assert_is_pattern(pattern)\n\n\ndef test_can_import_empty_pattern_from_path(temp_empty_pattern_path):\n    pattern = knittingpattern.load_from_path(temp_empty_pattern_path)\n    assert_is_pattern(pattern)\n\n\ndef test_knitting_pattern_type_is_present():\n    with raises(ValueError):\n        knittingpattern.load_from_object({})\n\n\ndef test_knitting_pattern_type_is_correct():\n    with raises(ValueError):\n        knittingpattern.load_from_object({\"type\": \"knitting pattern2\"})\n\n\ndef test_load_from_url(temp_empty_pattern_path):\n    url = \"file:///\" + temp_empty_pattern_path\n    pattern = knittingpattern.load_from_url(url)\n    assert_is_pattern(pattern)\n"
  },
  {
    "path": "knittingpattern/test/test_row_instructions.py",
    "content": "\"\"\"These tests access the instructions in rows.\"\"\"\nfrom pytest import fixture, raises\nfrom test_examples import charlotte as _charlotte\nfrom knittingpattern.Instruction import InstructionNotFoundInRow\nimport pytest\nfrom knittingpattern.Row import Row\nfrom unittest.mock import Mock\nfrom knittingpattern.Parser import default_parser\n\n\n@fixture\ndef a1():\n    \"\"\":return: the pattern ``\"A.1\"`` in charlotte\"\"\"\n    return _charlotte().patterns[\"A.1\"]\n\n\n@fixture\ndef row0(a1):\n    return a1.rows.at(0)\n\n\n@fixture\ndef row1(a1):\n    return a1.rows.at(1)\n\n\n@fixture\ndef row2(a1):\n    return a1.rows.at(2)\n\n\n@fixture\ndef mesh0(row0):\n    mesh = row0.produced_meshes[0]\n    return mesh\n\n\n@fixture\ndef instruction0(row0):\n    return row0.instructions[0]\n\n\n@fixture\ndef instruction1(row1):\n    return row1.instructions[0]\n\n\n@fixture\ndef removed_instruction(row0):\n    return row0.instructions.pop(1)\n\n\ndef test_row0_consumes_empty_meshes(row0):\n    assert len(row0.consumed_meshes) == 5\n    assert not any(mesh.is_produced() for mesh in row0.consumed_meshes)\n\n\ndef test_consumed_meshes_have_index(row0):\n    for i in range(5):\n        mesh = row0.consumed_meshes[i]\n        assert mesh.index_in_consuming_row == i\n        assert mesh.consuming_row == row0\n\n\ndef test_row0_produces_5_meshes(row0):\n    assert len(row0.produced_meshes) == 5\n    assert all(mesh.is_knit() for mesh in row0.produced_meshes)\n\n\ndef test_row0_meshes_point_also_to_row1(mesh0, row0, row1):\n    assert mesh0.producing_row == row0\n    assert mesh0.consuming_row == row1\n\n\ndef test_row0_instruction_produces_mesh_0(mesh0, instruction0):\n    assert instruction0 == mesh0.producing_instruction\n    assert instruction0.produced_meshes == [mesh0]\n    assert instruction0.number_of_produced_meshes == 1\n\n\ndef test_instruction0_is_knit(instruction0):\n    assert instruction0.does_knit()\n\n\ndef test_instruction_position_in_row(row0, instruction0):\n    assert instruction0.row == row0\n    assert instruction0.index_in_row == 0\n    assert row0.instructions[0] == instruction0\n\n\ndef test_mesh0_is_consumed_by_instruction1(mesh0, instruction1):\n    assert mesh0.consuming_instruction == instruction1\n    assert instruction1.consumed_meshes[0].as_produced_mesh() == mesh0\n\n\ndef test_instruction1_is_knit(instruction1):\n    assert instruction1.does_knit()\n\n\ndef test_instruction1_position_in_row(instruction1):\n    assert instruction1.index_in_row == 0\n\n\ndef test_mesh0_is_produced(mesh0):\n    assert mesh0.is_produced()\n    assert mesh0.is_consumed()\n\n\ndef test_instruction0_builds_on_unproduced_meshes(instruction0):\n    assert not instruction0.consumed_meshes[0].is_produced()\n\n\n@fixture\ndef skp(row2):\n    return row2.instructions[0]\n\n\n@fixture\ndef yo(row2):\n    return row2.instructions[1]\n\n\ndef test_yarn_over(yo):\n    assert yo.number_of_consumed_meshes == 0\n    assert yo.number_of_produced_meshes == 1\n\n\ndef test_skp(skp):\n    assert skp.number_of_consumed_meshes == 2\n    assert skp.number_of_produced_meshes == 1\n\n\ndef test_position_in_row2(skp, yo, row2):\n    assert skp.row == row2\n    assert yo.row == row2\n    assert skp.index_in_row == 0\n    assert yo.index_in_row == 1\n\n\ndef test_skp_consumed_meshes_from_row1(skp, row1, row2):\n    assert len(skp.produced_meshes) == 1\n    assert len(skp.consumed_meshes) == 2\n    m1, m2 = skp.consumed_meshes\n    assert m1.consuming_instruction == skp\n    assert m1.consuming_row == row2\n    assert m1.producing_row == row1\n    assert m1.index_in_producing_row == 0\n    assert m1.is_produced()\n    assert m1.is_consumed()\n    assert m2.consuming_instruction == skp\n    assert m2.consuming_row == row2\n    assert m2.producing_row == row1\n    assert m2.index_in_producing_row == 1\n    assert m2.index_in_consuming_row == 1\n\n\ndef test_skp_produces_one_mesh(skp):\n    assert len(skp.produced_meshes) == 1\n\n\ndef test_skp_produced_meshes(skp, row2):\n    m = skp.produced_meshes[0]\n    assert m.producing_instruction == skp\n    assert m.is_produced()\n    assert not m.is_consumed()\n    assert m.index_in_producing_row == 0\n    assert m.producing_row == row2\n\n\ndef test_yarn_over_consumes_no_meshes(yo):\n    assert yo.consumed_meshes == []\n\n\ndef test_yarn_over_produces_a_mesh(yo):\n    assert len(yo.produced_meshes) == 1\n    m = yo.produced_meshes[0]\n    assert m.producing_instruction == yo\n    assert m.producing_row == yo.row\n    assert m.index_in_producing_row == 1\n\n\ndef test_previous_instruction(row0, instruction0):\n    assert row0.instructions[1].previous_instruction_in_row == instruction0\n\n\ndef test_next_instruction(row0, instruction0):\n    assert instruction0.next_instruction_in_row == row0.instructions[1]\n\n\ndef test_previous_instruction_is_None_at_border(instruction0):\n    assert instruction0.previous_instruction_in_row is None\n\n\ndef test_next_instruction_is_None_at_border(row0):\n    assert row0.instructions[-1].next_instruction_in_row is None\n\n\ndef test_index_of_instruction_does_not_change(instruction0):\n    index1 = instruction0.index_in_row\n    index2 = instruction0.index_in_row\n    assert index1 == index2\n\n\ndef test_repr(instruction0):\n    string = repr(instruction0)\n    assert string.startswith(\"<\" + instruction0.__class__.__name__)\n    assert str(instruction0.index_in_row) in string\n    assert repr(instruction0.row) in string\n\n\ndef test_instruction_consumes_no_mesh_but_has_mesh_index(yo):\n    assert yo.index_of_first_consumed_mesh_in_row == 2\n    assert yo.index_of_last_consumed_mesh_in_row == 1\n\n\ndef test_index_of_last_produced_mesh_is_same_as_first(yo):\n    first = yo.index_of_first_produced_mesh_in_row\n    last = yo.index_of_last_produced_mesh_in_row\n    assert first == last\n\n\ndef test_removed_instruction_raises_exception(removed_instruction):\n    with raises(InstructionNotFoundInRow):\n        removed_instruction.index_of_first_produced_mesh_in_row\n    with raises(InstructionNotFoundInRow):\n        removed_instruction.index_of_last_produced_mesh_in_row\n    with raises(InstructionNotFoundInRow):\n        removed_instruction.index_of_first_consumed_mesh_in_row\n    with raises(InstructionNotFoundInRow):\n        removed_instruction.index_of_last_consumed_mesh_in_row\n\n\ndef test_instruction_is_in_row(instruction0):\n    assert instruction0.is_in_row()\n\n\ndef test_instruction_is_not_in_row(removed_instruction):\n    assert not removed_instruction.is_in_row()\n\n\ndef test_repr_removed_instruction(removed_instruction):\n    assert removed_instruction.__class__.__name__ in repr(removed_instruction)\n\n\ndef test_repr_meshes(instruction0, row2):\n    assert \"Mesh\" in repr(instruction0.produced_meshes[0])\n    assert \"Mesh\" in repr(instruction0.consumed_meshes[0])\n    assert \"Mesh\" in repr(row2.produced_meshes[0])\n\n\nclass TestShortAccess(object):\n\n    \"\"\"Test convenience methods and properties.\"\"\"\n\n    def test_first_instruction(self, a1):\n        for row in a1.rows:\n            assert row.first_instruction == row.instructions[0]\n\n    def test_last_instruction(self, a1):\n        for row in a1.rows:\n            assert row.last_instruction == row.instructions[-1]\n\n\nclass TestInstructionColors(object):\n\n    \"\"\"Test Row.instruction_colors.\"\"\"\n\n    @pytest.mark.parametrize(\"specs,result\", [\n        ([{}, {}], [None]), ([{\"color\": 1}], [1]),\n        ([{\"color\": 3}, {}, {\"color\": 123}, {\"color\": 123}], [3, None, 123])])\n    @pytest.mark.parametrize(\"row_spec,default_color\", [\n        ({\"color\": \"green\"}, \"green\"), ({}, None)])\n    def test_row_instructions(self, specs, result, row_spec, default_color):\n        result = result[:]\n        for i, color in enumerate(result):\n            if color is None:\n                result[i] = default_color\n        row = Row(\"id\", row_spec, default_parser())\n        for instruction in specs:\n            row.instructions.append(instruction)\n        assert row.instruction_colors == result\n"
  },
  {
    "path": "knittingpattern/test/test_row_mapping.py",
    "content": "\"\"\"Test that the rows of a pattern map the right way.\"\"\"\nfrom pytest import fixture\nfrom knittingpattern import load_from_object\nfrom knittingpattern.Loader import JSONLoader as Loader\n\nrelative_path = \"pattern/row_mapping_pattern.json\"\nrow_mapping_pattern1 = Loader().relative_file(__name__, relative_path)\n\n\n@fixture\ndef p1():\n    return load_from_object(row_mapping_pattern1)\n\n\n@fixture\ndef a1(p1):\n    return p1.patterns[\"A.1\"]\n\n\n@fixture\ndef r11(a1):\n    return a1.rows[\"1.1\"]\n\n\n@fixture\ndef r21(a1):\n    return a1.rows[\"2.1\"]\n\n\n@fixture\ndef r22(a1):\n    return a1.rows[\"2.2\"]\n\n\n@fixture\ndef r32(a1):\n    return a1.rows[\"3.2\"]\n\n\n@fixture\ndef r41(a1):\n    return a1.rows[\"4.1\"]\n\n\n# TODO: test _get_producing_row_and_index\n\ndef assert_rows_map(row1, index1, row2, index2):\n    produced_mesh = row1.produced_meshes[index1]\n    consumed_mesh = row2.consumed_meshes[index2]\n    assert produced_mesh.is_connected_to(consumed_mesh)\n\n\ndef assert_is_not_connected(row, index):\n    assert not row.produced_meshes[index].is_connected()\n\n\nclass TestRow11:\n\n    def test_first_meshes_map_to_second_row(self, r11, r21):\n        assert_rows_map(r11, 0, r21, 0)\n        assert_rows_map(r11, 1, r21, 1)\n\n    def test_middle_mesh_does_not_map_to_any_row(self, r11):\n        assert_is_not_connected(r11, 2)\n\n    def test_right_meshes_map_to_third_row(self, r11, r22):\n        assert_rows_map(r11, 3, r22, 0)\n        assert_rows_map(r11, 4, r22, 1)\n\n    def test_number_of_meshes(self, r11):\n        assert r11.number_of_produced_meshes == 5\n        assert r11.number_of_consumed_meshes == 4\n\n\nclass TestRow21:\n\n    def test_all_meshes_map_to_last_row(self, r21, r41):\n        assert_rows_map(r21, 0, r41, 0)\n        assert_rows_map(r21, 1, r41, 1)\n\n    def test_number_of_meshes(self, r21):\n        assert r21.number_of_produced_meshes == 2\n        assert r21.number_of_consumed_meshes == 2\n\n\nclass TestRow22:\n\n    def test_all_meshes_map_to_row_3(self, r22, r32):\n        assert_rows_map(r22, 0, r32, 0)\n        assert_rows_map(r22, 1, r32, 1)\n\n\nclass TestRow32:\n\n    def test_all_meshes_map_to_last_row(self, r32, r41):\n        assert_rows_map(r32, 0, r41, 3)\n        assert_rows_map(r32, 1, r41, 4)\n\n\nclass TestRow41:\n\n    def test_row_maps_to_nowhere(self, r41):\n        for i in range(4):\n            assert_is_not_connected(r41, i)\n\n    def test_number_of_meshes(self, r41):\n        assert r41.number_of_produced_meshes == 4\n        assert r41.number_of_consumed_meshes == 5\n"
  },
  {
    "path": "knittingpattern/test/test_row_meshes.py",
    "content": "from pytest import fixture, raises\nfrom knittingpattern import new_knitting_pattern\n\nNO_CONSUMED_MESH = {\"number of consumed meshes\": 0}\nNO_PRODUCED_MESH = {\"number of produced meshes\": 0}\nDOUBLE_CONSUMED_MESH = {\"number of consumed meshes\": 2}\nDOUBLE_PRODUCED_MESH = {\"number of produced meshes\": 2}\n\n\ndef assert_consumed_index(mesh, instruction_index, index_in_instruction=0):\n    assert mesh.consuming_instruction.index_in_row == instruction_index\n    assert mesh.index_in_consuming_instruction == index_in_instruction\n\n\ndef assert_produced_index(mesh, instruction_index, index_in_instruction=0):\n    assert mesh.producing_instruction.index_in_row == instruction_index\n    assert mesh.index_in_producing_instruction == index_in_instruction\n\n\ndef assert_row(row, first_consumed, last_consumed, first_produced,\n               last_produced):\n    assert_consumed_index(row.first_consumed_mesh, *first_consumed)\n    assert_consumed_index(row.last_consumed_mesh, *last_consumed)\n    assert_produced_index(row.first_produced_mesh, *first_produced)\n    assert_produced_index(row.last_produced_mesh, *last_produced)\n\n\n@fixture\ndef row():\n    pattern = new_knitting_pattern(\"test\")\n    return pattern.add_row(1)\n\n\ndef test_no_meshes(row):\n    with raises(IndexError):\n        row.first_consumed_mesh\n    with raises(IndexError):\n        row.last_consumed_mesh\n    with raises(IndexError):\n        row.first_produced_mesh\n    with raises(IndexError):\n        row.last_produced_mesh\n\n\ndef test_knit_row(row):\n    row.instructions.extend([{}, {}, {}, {}])\n    assert_row(row, (0,), (3,), (0,), (3,))\n\n\ndef test_1_or_0(row):\n    row.instructions.extend([NO_CONSUMED_MESH, {}, NO_PRODUCED_MESH])\n    assert_row(row, (1,), (2,), (0,), (1,))\n\n\ndef test_2(row):\n    row.instructions.extend([DOUBLE_CONSUMED_MESH, {}, DOUBLE_PRODUCED_MESH])\n    assert_row(row, (0,), (2,), (0,), (2, 1))\n\n\ndef test_2_reversed(row):\n    row.instructions.extend([DOUBLE_PRODUCED_MESH, {}, DOUBLE_CONSUMED_MESH])\n    assert_row(row, (0,), (2, 1), (0,), (2,))\n"
  },
  {
    "path": "knittingpattern/test/test_utilities.py",
    "content": "from knittingpattern.utils import unique\nimport pytest\n\n\nclass TestUniquenes(object):\n\n    \"\"\"Test the function unique.\"\"\"\n\n    @pytest.mark.parametrize(\"input,expected_result\", [\n        ([], []), ([[1, 1, 1, 1, 1]], [1]),\n        ([[1, 2, 3], [4, 3, 2, 1]], [1, 2, 3, 4]),\n        ([[None, 4], [4, 6, None]], [None, 4, 6])])\n    @pytest.mark.parametrize(\"use_generator\", [True, False])\n    def test_results(self, input, expected_result, use_generator):\n        if use_generator:\n            input = [(element for element in listing) for listing in input]\n        result = unique(input)\n        assert result == expected_result\n"
  },
  {
    "path": "knittingpattern/test/test_walk.py",
    "content": "\"\"\"The the ability to sort rows in an order so they can  be knit.\"\"\"\nimport pytest\nfrom knittingpattern import load_from_relative_file, new_knitting_pattern\nfrom knittingpattern.walk import walk\n\n\ndef walk_ids(pattern):\n    return list(map(lambda row: row.id, walk(pattern)))\n\n\n@pytest.mark.parametrize(\"pattern_file,expected_ids\", [\n    (\"inheritance.json\", [\"colored\", \"inherited uncolored +instructions\",\n                          \"inherited colored +instructions\", \"uncolored\",\n                          \"inherited uncolored\", \"inherited colored\"]),\n    (\"row_mapping_pattern.json\", [\"1.1\", \"2.1\", \"2.2\", \"3.2\", \"4.1\"]),\n    (\"row_removal_pattern.json\", [1, 2, 3]),\n    (\"single_instruction.json\", [1, 2])])\ndef test_test_patterns(pattern_file, expected_ids):\n    patterns = load_from_relative_file(__name__, \"pattern/\" + pattern_file)\n    pattern = patterns.patterns.at(0)\n    walked_ids = walk_ids(pattern)\n    assert walked_ids == expected_ids\n\n\ndef construct_graph(links):\n    pattern = new_knitting_pattern(\"constructed_graph\")\n    rows = pattern.rows\n    for link in links:\n        for row_id in link:\n            if row_id not in rows:\n                pattern.add_row(row_id)\n    for from_id, *to_ids in links:\n        from_row = rows[from_id]\n        for to_id in to_ids:\n            to_row = rows[to_id]\n            from_row.instructions.append({})\n            to_row.instructions.append({})\n            from_row.last_produced_mesh.connect_to(to_row.last_consumed_mesh)\n    return pattern\n\n\n@pytest.mark.parametrize(\"links,expected_ids\", [\n    (((1, 2, 3), (2, 3), (4, 1)), [4, 1, 2, 3]),\n    (((4, 1, 2), (2, 3, 5), (5, 6), (3, 0), (0, 6)), [4, 1, 2, 3, 0, 5, 6]),\n    (((8, 6, 4, 2, 0), (6, 5), (4, 5), (2, 1), (0, 1), (1, 7), (5, 7), (7, 9)),\n     [8, 6, 4, 5, 2, 0, 1, 7, 9]),\n    (((3, 1, 2), (1, 1.1), (1.1, 1.2), (2, 2.1), (2.1, 2.2), (2.2, 4),\n      (1.2, 4)), [3, 1, 1.1, 1.2, 2, 2.1, 2.2, 4])])\ndef test_graphs_are_sorted(links, expected_ids):\n    pattern = construct_graph(links)\n    walked_ids = walk_ids(pattern)\n    assert walked_ids == expected_ids\n"
  },
  {
    "path": "knittingpattern/utils.py",
    "content": "\"\"\"This module contains some useful functions.\n\nThe functions work on the standard library or are not specific to\na certain existing module.\n\"\"\"\n\n\ndef unique(iterables):\n    \"\"\"Create an iterable from the iterables that contains each element once.\n\n    :return: an iterable over the iterables. Each element of the result\n      appeared only once in the result. They are ordered by the first\n      occurrence in the iterables.\n    \"\"\"\n    included_elements = set()\n\n    def included(element):\n        result = element in included_elements\n        included_elements.add(element)\n        return result\n    return [element for elements in iterables for element in elements\n            if not included(element)]\n\n\n__all__ = [\"unique\"]\n"
  },
  {
    "path": "knittingpattern/walk.py",
    "content": "\"\"\"Walk the knitting pattern.\"\"\"\n\n\ndef walk(knitting_pattern):\n    \"\"\"Walk the knitting pattern in a right-to-left fashion.\n\n    :return: an iterable to walk the rows\n    :rtype: list\n    :param knittingpattern.KnittingPattern.KnittingPattern knitting_pattern: a\n      knitting pattern to take the rows from\n    \"\"\"\n    rows_before = {}  # key consumes from values\n    free_rows = []\n    walk = []\n    for row in knitting_pattern.rows:\n        rows_before_ = row.rows_before[:]\n        if rows_before_:\n            rows_before[row] = rows_before_\n        else:\n            free_rows.append(row)\n    assert free_rows\n    while free_rows:\n        # print(\"free rows:\", free_rows)\n        row = free_rows.pop(0)\n        walk.append(row)\n        assert row not in rows_before\n        for freed_row in reversed(row.rows_after):\n            todo = rows_before[freed_row]\n            # print(\"  freed:\", freed_row, todo)\n            todo.remove(row)\n            if not todo:\n                del rows_before[freed_row]\n                free_rows.insert(0, freed_row)\n    assert not rows_before, \"everything is walked\"\n    return walk\n\n\n__all__ = [\"walk\"]\n"
  },
  {
    "path": "pylintrc",
    "content": "[MASTER]\n\n# Specify a configuration file.\n#rcfile=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=CVS\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# List of plugins (as comma separated values of python modules names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Use multiple processes to speed up Pylint.\njobs=1\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code\nextension-pkg-whitelist=\n\n# Allow optimization of some AST trees. This will activate a peephole AST\n# optimizer, which will apply various small optimizations. For instance, it can\n# be used to obtain the result of joining multiple strings with the addition\n# operator. Joining a lot of strings can lead to a maximum recursion error in\n# Pylint and this flag can prevent that. It has one side effect, the resulting\n# AST will be different than the one from reality.\noptimize-ast=no\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED\nconfidence=HIGH\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time (only on the command line, not in the configuration file where\n# it should appear only once). See also the \"--disable\" option for examples.\n#enable=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once).You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use\"--disable=all --enable=classes\n# --disable=W\"\n#disable=long-suffix,dict-view-method,hex-method,cmp-builtin,unicode-builtin,suppressed-message,old-division,execfile-builtin,file-builtin,getslice-method,indexing-exception,backtick,map-builtin-not-iterating,unichr-builtin,next-method-called,intern-builtin,old-raise-syntax,setslice-method,basestring-builtin,standarderror-builtin,delslice-method,long-builtin,useless-suppression,zip-builtin-not-iterating,reload-builtin,metaclass-assignment,coerce-method,raw_input-builtin,nonzero-method,reduce-builtin,dict-iter-method,apply-builtin,filter-builtin-not-iterating,cmp-method,round-builtin,input-builtin,coerce-builtin,range-builtin-not-iterating,old-octal-literal,buffer-builtin,unpacking-in-except,using-cmp-argument,raising-string,parameter-unpacking,oct-method,print-statement,import-star-module-level,old-ne-operator,xrange-builtin,no-absolute-import\ndisable=\n\n\n[REPORTS]\n\n# Set the output format. Available formats are text, parseable, colorized, msvs\n# (visual studio) and html. You can also give a reporter class, eg\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Put messages in a separate file for each module / package specified on the\n# command line instead of printing them on stdout. Reports (if any) will be\n# written in a file name \"pylint_global.[txt|html]\".\nfiles-output=no\n\n# Tells whether to display a full report or only the messages\nreports=yes\n\n# Python expression which should return a note less than 10 (10 is the highest\n# note). You have access to the variables errors warning, statement which\n# respectively contain the number of errors / warnings messages and the total\n# number of statements analyzed. This is used by the global evaluation report\n# (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details\n#msg-template=\n\n\n[BASIC]\n\n# List of builtins function names that should not be used, separated by a comma\nbad-functions=map,filter\n\n# Good variable names which should always be accepted, separated by a comma\ngood-names=i,j,k,ex,Run,_,x,y\n\n# Bad variable names which should always be refused, separated by a comma\nbad-names=foo,bar,baz,toto,tutu,tata,temp\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Include a hint for the correct naming format with invalid-name\ninclude-naming-hint=yes\n\n# Regular expression matching correct module names\nmodule-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Naming hint for module names\nmodule-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Regular expression matching correct method names\nmethod-rgx=[a-z_][a-z0-9_]{2,70}$\n\n# Naming hint for method names\nmethod-name-hint=[a-z_][a-z0-9_]{2,70}$\n\n# Regular expression matching correct constant names\nconst-rgx=((?P<allUpper>([A-Z_][A-Z0-9_]*)|(__.*__))|(?P<allLower>([a-z_][a-z0-9_]*)|(__.*__)))$\n\n# Naming hint for constant names\nconst-name-hint=((?P<allUpper>([A-Z_][A-Z0-9_]*)|(__.*__))|(?P<allLower>([a-z_][a-z0-9_]*)|(__.*__)))$\n\n# Regular expression matching correct class attribute names\nclass-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,70}|(__.*__))$\n\n# Naming hint for class attribute names\nclass-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,70}|(__.*__))$\n\n# Regular expression matching correct class names\nclass-rgx=[A-Z_][a-zA-Z0-9]+$\n\n# Naming hint for class names\nclass-name-hint=[A-Z_][a-zA-Z0-9]+$\n\n# Regular expression matching correct attribute names\nattr-rgx=[a-z_][a-z0-9_]{2,70}$\n\n# Naming hint for attribute names\nattr-name-hint=[a-z_][a-z0-9_]{2,70}$\n\n# Regular expression matching correct inline iteration names\ninlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$\n\n# Naming hint for inline iteration names\ninlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$\n\n# Regular expression matching correct function names\nfunction-rgx=[a-z_][a-z0-9_]{2,70}$\n\n# Naming hint for function names\nfunction-name-hint=[a-z_][a-z0-9_]{2,70}$\n\n# Regular expression matching correct argument names\nargument-rgx=[a-z_][a-z0-9_]{2,70}$\n\n# Naming hint for argument names\nargument-name-hint=[a-z_][a-z0-9_]{2,70}$\n\n# Regular expression matching correct variable names\nvariable-rgx=[a-z_][a-z0-9_]{2,70}$\n\n# Naming hint for variable names\nvariable-name-hint=[a-z_][a-z0-9_]{2,70}$\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=^_\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n\n[ELIF]\n\n# Maximum number of nested blocks for function / method body\nmax-nested-blocks=5\n\n\n[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=79\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n# List of optional constructs for which whitespace checking is disabled. `dict-\n# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\\n222: 2}.\n# `trailing-comma` allows a space between comma and closing bracket: (a, ).\n# `empty-line` allows space-only lines.\nno-space-check=trailing-comma,dict-separator\n\n# Maximum number of lines in a module\nmax-module-lines=1000\n\n# String used as indentation unit. This is usually \"    \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Number of spaces of indent required inside a hanging  or continued line.\nindent-after-paren=4\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n\n[LOGGING]\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format\nlogging-modules=logging\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,XXX,TODO\n\n\n[SIMILARITIES]\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=4\n\n# Ignore comments when computing similarities.\nignore-comments=yes\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=yes\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n\n[SPELLING]\n\n# Spelling dictionary name. Available dictionaries: none. To make it working\n# install python-enchant package.\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to indicated private dictionary in\n# --spelling-private-dict-file option instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[TYPECHECK]\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis. It\n# supports qualified module names, as well as Unix pattern matching.\nignored-modules=\n\n# List of classes names for which member attributes should not be checked\n# (useful for classes with attributes dynamically set). This supports can work\n# with qualified names.\nignored-classes=\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E1101 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=\n\n\n[VARIABLES]\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# A regular expression matching the name of dummy variables (i.e. expectedly\n# not used).\ndummy-variables-rgx=_$|dummy\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid to define new builtins when possible.\nadditional-builtins=\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,_cb\n\n\n[CLASSES]\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,__new__,setUp\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=mcs\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,_fields,_replace,_source,_make\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method\nmax-args=5\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore\nignored-argument-names=_.*\n\n# Maximum number of locals for function / method body\nmax-locals=15\n\n# Maximum number of return / yield for function / method body\nmax-returns=6\n\n# Maximum number of branch for function / method body\nmax-branches=12\n\n# Maximum number of statements in function / method body\nmax-statements=50\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=7\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=2\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n# Maximum number of boolean expressions in a if statement\nmax-bool-expr=5\n\n\n[IMPORTS]\n\n# Deprecated modules which should not be used, separated by a comma\ndeprecated-modules=optparse\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled)\nimport-graph=\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled)\next-import-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled)\nint-import-graph=\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"Exception\"\novergeneral-exceptions=Exception\n"
  },
  {
    "path": "requirements.in",
    "content": "setuptools\nPillow\nwebcolors\nxmltodict\n"
  },
  {
    "path": "requirements.txt",
    "content": "#\n# This file is autogenerated by pip-compile\n# To update, run:\n#\n#    pip-compile --output-file requirements.txt requirements.in\n#\nappdirs==1.4.2            # via setuptools\npackaging==16.8           # via setuptools\npillow==3.2.0\npyparsing==2.1.10         # via packaging\nsix==1.10.0               # via packaging, setuptools\nwebcolors==1.5\nxmltodict==0.10.2\nObservableList==0.0.3\n\n# The following packages are considered to be unsafe in a requirements file:\n# setuptools\n"
  },
  {
    "path": "setup.cfg",
    "content": "[upload_docs]\nupload-dir=build/html\n\n[build_sphinx]\nsource-dir = docs/\nbuild-dir  = build/\nall_files  = 1\n\n[upload_sphinx]\nupload-dir = build/html\n\n[pytest]\n# see https://pypi.python.org/pypi/pytest-flakes\nflakes-ignore =\n    test_*.py UnusedImport RedefinedWhileUnused\n    *.py\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/python3\n\"\"\"The setup and build script for the library named \"PACKAGE_NAME\".\"\"\"\nimport os\nimport sys\nfrom setuptools.command.test import test as TestCommandBase\nfrom distutils.core import Command\nimport subprocess\n\nPACKAGE_NAME = \"knittingpattern\"\nPACKAGE_NAMES = [\n        \"knittingpattern\",\n        \"knittingpattern.convert\", \"knittingpattern.convert.test\"\n    ]\n\nHERE = os.path.abspath(os.path.dirname(__file__))\nsys.path.insert(0, HERE)  # for package import\n\n__version__ = __import__(PACKAGE_NAME).__version__\n__author__ = 'Nicco Kunzmann'\n\n\ndef read_file_named(file_name):\n    file_path = os.path.join(HERE, file_name)\n    with open(file_path) as file:\n        return file.read()\n\n\ndef read_requirements_file(file_name):\n    content = read_file_named(file_name)\n    lines = []\n    for line in content.splitlines():\n        comment_index = line.find(\"#\")\n        if comment_index >= 0:\n            line = line[:comment_index]\n        line = line.strip()\n        if not line:\n            continue\n        lines.append(line)\n    return lines\n\n\n# The base package metadata to be used by both distutils and setuptools\nMETADATA = dict(\n    name=PACKAGE_NAME,\n    version=__version__,\n    packages=PACKAGE_NAMES,\n    author=__author__,\n    author_email='niccokunzmann@rambler.ru',\n    description='Python library for knitting machines.',\n    license='LGPL',\n    url='https://github.com/fossasia/' + PACKAGE_NAME,\n    keywords='knitting ayab fashion',\n)\n\n# Run tests in setup\n\n\nclass TestCommand(TestCommandBase):\n\n    TEST_ARGS = [PACKAGE_NAME]\n\n    def finalize_options(self):\n        TestCommandBase.finalize_options(self)\n        self.test_suite = False\n\n    def run_tests(self):\n        import pytest\n        errcode = pytest.main(self.TEST_ARGS)\n        sys.exit(errcode)\n\n\nclass CoverageTestCommand(TestCommand):\n    TEST_ARGS = [PACKAGE_NAME, \"--cov=\" + PACKAGE_NAME]\n\n\nclass PEP8TestCommand(TestCommand):\n    TEST_ARGS = [PACKAGE_NAME, \"--pep8\"]\n\n\nclass FlakesTestCommand(TestCommand):\n    TEST_ARGS = [PACKAGE_NAME, \"--flakes\"]\n\n\nclass CoveragePEP8TestCommand(TestCommand):\n    TEST_ARGS = [PACKAGE_NAME, \"--cov=\" + PACKAGE_NAME, \"--pep8\"]\n\n\nclass LintCommand(TestCommandBase):\n\n    TEST_ARGS = [PACKAGE_NAME]\n\n    def finalize_options(self):\n        TestCommandBase.finalize_options(self)\n        self.test_suite = False\n\n    def run_tests(self):\n        from pylint.lint import Run\n        Run(self.TEST_ARGS)\n\n\n# command for linking\n\n\nclass LinkIntoSitePackagesCommand(Command):\n\n    description = \"link this module into the site-packages so the latest \"\\\n        \"version can always be used without installation.\"\n    user_options = []\n    library_path = os.path.join(HERE, PACKAGE_NAME)\n    site_packages = [p for p in sys.path if \"site-packages\" in p]\n\n    def initialize_options(self):\n        pass\n\n    def finalize_options(self):\n        pass\n\n    def run(self):\n        assert self.site_packages, \"We need a folder to install to.\"\n        print(\"link: {} -> {}\".format(\n                  os.path.join(self.site_packages[0], PACKAGE_NAME),\n                  self.library_path\n              ))\n        try:\n            if \"win\" in sys.platform:\n                self.run_windows_link()\n            elif \"linux\" == sys.platform:\n                self.run_linux_link()\n            else:\n                self.run_other_link()\n        except:\n            print(\"failed:\")\n            raise\n        else:\n            print(\"linked\")\n\n    def run_linux_link(self):\n        subprocess.check_call([\"sudo\", \"ln\", \"-f\", \"-s\", \"-t\",\n                               self.site_packages[0], self.library_path])\n\n    run_other_link = run_linux_link\n\n    def run_windows_link(self):\n        path = os.path.join(self.site_packages[0], PACKAGE_NAME)\n        if os.path.exists(path):\n            os.remove(path)\n        command = [\"mklink\", \"/J\", path, self.library_path]\n        subprocess.check_call(command, shell=True)\n\n# Extra package metadata to be used only if setuptools is installed\n\nrequired_packages = read_requirements_file(\"requirements.txt\")\nrequired_test_packages = read_requirements_file(\"test-requirements.txt\")\n\n# print requirements\n\n\nclass PrintRequiredPackagesCommand(Command):\n\n    description = \"Print the packages to install. \"\\\n                  \"Use pip install `setup.py requirements`\"\n    user_options = []\n    name = \"requirements\"\n\n    def initialize_options(self):\n        pass\n\n    def finalize_options(self):\n        pass\n\n    @staticmethod\n    def run():\n        packages = list(set(required_packages + required_test_packages))\n        packages.sort(key=lambda s: s.lower())\n        for package in packages:\n            print(package)\n\n# set development status from __version__\n\nDEVELOPMENT_STATES = {\n        \"p\": \"Development Status :: 1 - Planning\",\n        \"pa\": \"Development Status :: 2 - Pre-Alpha\",\n        \"a\": \"Development Status :: 3 - Alpha\",\n        \"b\": \"Development Status :: 4 - Beta\",\n        \"\": \"Development Status :: 5 - Production/Stable\",\n        \"m\": \"Development Status :: 6 - Mature\",\n        \"i\": \"Development Status :: 7 - Inactive\"\n    }\ndevelopment_state = DEVELOPMENT_STATES[\"\"]\nfor ending in DEVELOPMENT_STATES:\n    if ending and __version__.endswith(ending):\n        development_state = DEVELOPMENT_STATES[ending]\n\nif not __version__[-1:].isdigit():\n    METADATA[\"version\"] += \"0\"\n\n# tag and upload to github to autodeploy with travis\n\n\nclass TagAndDeployCommand(Command):\n\n    description = \"Create a git tag for this version and push it to origin.\"\\\n                  \"To trigger a travis-ci build and and deploy.\"\n    user_options = []\n    name = \"tag_and_deploy\"\n    remote = \"origin\"\n    branch = \"master\"\n\n    def initialize_options(self):\n        pass\n\n    def finalize_options(self):\n        pass\n\n    def run(self):\n        if subprocess.call([\"git\", \"--version\"]) != 0:\n            print(\"ERROR:\\n\\tPlease install git.\")\n            exit(1)\n        status_lines = subprocess.check_output([\"git\", \"status\"]).splitlines()\n        current_branch = status_lines[0].strip().split()[-1].decode()\n        print(\"On branch {}.\".format(current_branch))\n        if current_branch != self.branch:\n            print(\"ERROR:\\n\\tNew tags can only be made from branch \\\"{}\\\".\"\n                  \"\".format(self.branch))\n            print(\"\\tYou can use \\\"git checkout {}\\\" to switch the branch.\"\n                  \"\".format(self.branch))\n            exit(1)\n        tags_output = subprocess.check_output([\"git\", \"tag\"])\n        tags = [tag.strip().decode() for tag in tags_output.splitlines()]\n        tag = \"v\" + __version__\n        if tag in tags:\n            print(\"Warning: \\n\\tTag {} already exists.\".format(tag))\n            print(\"\\tEdit the version information in {}\".format(\n                    os.path.join(HERE, PACKAGE_NAME, \"__init__.py\")\n                ))\n        else:\n            print(\"Creating tag \\\"{}\\\".\".format(tag))\n            subprocess.check_call([\"git\", \"tag\", tag])\n        print(\"Pushing tag \\\"{}\\\" to remote \\\"{}\\\".\".format(tag, self.remote))\n        subprocess.check_call([\"git\", \"push\", self.remote, tag])\n\n\nSETUPTOOLS_METADATA = dict(\n    install_requires=required_packages,\n    tests_require=required_test_packages,\n    include_package_data=True,\n    classifiers=[  # https://pypi.python.org/pypi?%3Aaction=list_classifiers\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: GNU Lesser General Public License'\n        ' v3 (LGPLv3)',\n        'Topic :: Software Development :: Libraries :: Python Modules',\n        'Topic :: Artistic Software',\n        'Topic :: Home Automation',\n        'Topic :: Utilities',\n        'Intended Audience :: Manufacturing',\n        'Natural Language :: English',\n        'Operating System :: OS Independent',\n        'Programming Language :: Python :: 3 :: Only',\n        development_state\n        ],\n    package_data=dict(\n        # If any package contains of these files, include them:\n        knitting=['*.json'],\n    ),\n    zip_safe=False,\n    cmdclass={\n        \"test\": TestCommand,\n        \"coverage\": CoverageTestCommand,\n        \"coverage_test\": CoverageTestCommand,\n        \"pep8\": PEP8TestCommand,\n        \"pep8_test\": PEP8TestCommand,\n        \"flakes\": FlakesTestCommand,\n        \"fakes_test\": FlakesTestCommand,\n        \"coverage_pep8_test\": CoveragePEP8TestCommand,\n        \"lint\": LintCommand,\n        \"link\": LinkIntoSitePackagesCommand,\n        PrintRequiredPackagesCommand.name: PrintRequiredPackagesCommand,\n        TagAndDeployCommand.name: TagAndDeployCommand\n        },\n)\n\n\ndef main():\n    # Build the long_description from the README and CHANGES\n    METADATA['long_description'] = read_file_named(\"README.rst\")\n\n    # Use setuptools if available, otherwise fallback and use distutils\n    try:\n        import setuptools\n        METADATA.update(SETUPTOOLS_METADATA)\n        setuptools.setup(**METADATA)\n    except ImportError:\n        import distutils.core\n        distutils.core.setup(**METADATA)\n\nif __name__ == '__main__':\n    if len(sys.argv) == 2 and sys.argv[1] == PrintRequiredPackagesCommand.name:\n        PrintRequiredPackagesCommand.run()\n    else:\n        main()\n"
  },
  {
    "path": "test-requirements.in",
    "content": "pytest\npytest-cov\npytest-flakes\npylint\npytest-pep8\ncodeclimate-test-reporter\nuntangle\nsphinx\nsphinx-paramlinks\nsphinx_rtd_theme"
  },
  {
    "path": "test-requirements.txt",
    "content": "#\n# This file is autogenerated by pip-compile\n# To update, run:\n#\n#    pip-compile --output-file test-requirements.txt test-requirements.in\n#\nalabaster==0.7.8          # via sphinx\napipkg==1.4               # via execnet\nastroid==1.4.6            # via pylint\nbabel==2.3.4              # via sphinx\ncodeclimate-test-reporter==0.1.1\ncolorama==0.3.7           # via pylint\ncoverage==4.1             # via codeclimate-test-reporter, pytest-cov\ndocutils==0.12            # via sphinx\nexecnet==1.4.1            # via pytest-cache\nimagesize==0.7.1          # via sphinx\njinja2==2.8               # via sphinx\nlazy-object-proxy==1.2.2  # via astroid\nmarkupsafe==0.23          # via jinja2\npep8==1.7.0               # via pytest-pep8\npy==1.4.31                # via pytest\npyflakes==1.2.3           # via pytest-flakes\npygments==2.1.3           # via sphinx\npylint==1.5.5\npytest-cache==1.0         # via pytest-flakes, pytest-pep8\npytest-cov==2.2.1\npytest-flakes==1.0.1\npytest-pep8==1.0.6\npytest==2.9.1\npytz==2016.4              # via babel\nrequests==2.10.0          # via codeclimate-test-reporter\nsix==1.10.0               # via astroid, pylint, sphinx\nsnowballstemmer==1.2.1    # via sphinx\nsphinx-paramlinks==0.3.2\nsphinx-rtd-theme==0.1.9\nsphinx==1.4.4\nuntangle==1.1.0\nwrapt==1.10.8             # via astroid\n"
  }
]