Metadata-Version: 2.0
Name: mistool
Version: 1.1.1b0
Summary: Miscellaneous missing tools that can help the py-developper.
Home-page: https://github.com/bc-python-tools/mistool
Author: Christophe BAL
Author-email: projetmbc@gmail.com
License: GPLv3
Keywords: python latex os path string terminal tool
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Topic :: Utilities
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Desktop Environment :: File Managers
Classifier: Topic :: System :: Logging
Classifier: Topic :: Text Processing :: Markup :: LaTeX

What about this package ?
=========================

**misTool** is the contraction of **missing**, **miscellaneous** and
**tool(s)**. This package contains some modules that could be useful for
Python developments.

***If you want more informations and examples than thereafter, just take
a look at the docstrings.***

I beg your pardon for my english...
===================================

English is not my native language, so be nice if you notice
misunderstandings, misspellings or grammatical errors in my documents
and codes.

What's new in this version ``1.1.1-beta`` ?
===========================================

In the module ``python_use``, the new class ``MKOrderedDict`` allows to
define kinds of ordered dictionaries accepting several times the same
key but at different "places".

**Warning !** Here are some important changes.

1. In the module ``python_use``, the class ``OrderedRecuDict`` becomes
   ``RecuOrderedDict``.

2. In the module ``string_use``, the function ``ascii_it`` becomes
   ``asciify``.

The module ``os_use``
=====================

Changing the working directory for commands
-------------------------------------------

With ``os_use.cd``, you have a context which changes temporarily the
directory where launching terminal like commands. When the context is
closed, the working directory goes back to the one just before the call
of ``os_use.cd``.

Let's see an example. We suppose that we have the following directory
with the absolute path ``/Users/projetmbc/basic_dir`` in a Unix system.

::

    + basic_dir
        * latex_1.tex
        * latex_2.tex
        * python_1.py
        * python_2.py
        * python_3.py
        * python_4.py
        * text_1.txt
        * text_2.txt
        * text_3.txt
        + empty_dir
        + sub_dir
            * code_A.py
            * code_B.py
            * slide_A.pdf
            * slide_B.pdf
            + sub_sub_dir
                * doc.pdf

The following code first goes inside ``/Users/projetmbc/basic_dir`` and
then it moves to ``/Users/projetmbc/basic_dir/sub_dir``. With
``subprocess.call("ls")``, we simply use the Unix command ``ls`` so as
to list files and folders inside the current working directory.

.. code:: python

    >>> import subprocess
    >>> from mistool.os_use import cd
    >>> with cd("/Users/projetmbc/basic_dir"):
    ...     subprocess.call("ls")
    empty_dir   python_1.py python_4.py text_2.txt
    latex_1.tex python_2.py sub_dir     text_3.txt
    latex_2.tex python_3.py text_1.txt
    >>> with cd("/Users/projetmbc/basic_dir/sub_dir"):
    ...     subprocess.call("ls")
    code_A.py   slide_A.pdf sub_sub_dir
    code_B.py   slide_B.pdf

Launching commands like in a terminal
-------------------------------------

The aim of the function ``os_use.runthis`` is to simplify a lot the
launching of subprocesses *(just use commands as you were inside your
terminal)*. Let's consider the basic following Python script with
absolute path ``/Users/projetmbc/script.py``.

.. code:: python

    print("Everything is ok.")

To launch this program, we just have to use the single string Unix
command ``python3 /Users/projetmbc/script.py`` like in the following
lines. You can see that by default nothing is printed, so you have to
use ``showoutput = True`` if you want to see what the script launched
prints.

.. code:: python

    >>> from mistool.os_use import PPath, runthis
    >>> pyfile = PPath("/Users/projetmbc/script.py")
    >>> runthis(cmd = "python3 {0}".format(ppath))
    >>> runthis(cmd = "python3 {0}".format(ppath), showoutput = True)
    Everything is ok.

System used and environment's path
----------------------------------

The call to ``os_use.system()`` returns the name, in lower case, of the
OS used : possible strings returned can be for example ``"windows"``,
``"mac"``, ``"linux"`` and also ``"java"``.

``os_use.pathenv()`` gives you the paths of executables known by your OS
*(this is indeed an alias for ``os.getenv('PATH')``)*.

Enhanced version of the class ``pathlib.Path``
----------------------------------------------

The class ``os_use.PPath`` adds several methods to the useful class
``pathlib.Path``. Here are examples.

Informations about one path
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following code shows additional informations given by the class
``os_use.PPath``.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> path = PPath("dir/subdir/file.txt")
    >>> path.parent
    PPath('dir/subdir')
    >>> print(path.depth)
    2
    >>> print(path.ext)
    'txt'

Another useful method named ``is_protected`` works as explained below.

1. If the path does not point to an existing file or folder, an OS error
   is raised.

2. If the path is the one of a folder, the answer returned is ``True``
   for a modifiable directory and ``False`` otherwise.

3. Finally if the path points to a file, then that is its parent folder
   which is tested.

There is also the method ``is_empty`` which can give three different
responses.

1. If the path is the one of an empty directory, ``False`` is returned.

2. ``True`` is returned when the path corresponds to an non-empty
   folder.

3. If the path doesn't point to an existing directory an OS error is
   raised.

Changing one path
~~~~~~~~~~~~~~~~~

Changing or adding an extension is very easy with the method
``with_ext``.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> path_no_ext = PPath("dir/subdir")
    >>> path_no_ext.with_ext("ext")
    PPath('dir/subdir.ext')
    >>> path_ext = PPath("dir/subdir/file.txt")
    >>> path_ext.with_ext("ext")
    PPath('dir/subdir/file.ext')

Obtaining a short version or a normalized one of a path needs no effort.
Here is how to do that *(``~`` is a shortcut for the main OS user's
folder)*.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> path_too_long = PPath("~/dir_1/dir_2/dir_3/../../file.txt")
    >>> path_too_long.normpath
    PPath('/Users/projetmbc/dir_1/file.txt')
    >>> path_long = PPath("/Users/projetmbc/dir_1/dir_2/dir_3/../../file.txt")
    >>> path_long.shortpath
    PPath('~/dir_1/file.txt')

Comparing paths
~~~~~~~~~~~~~~~

The "common" folder of several paths is obtained by using the method
``common_with`` or equivalently the magic operator ``&``.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> path        = PPath("/Users/projetmbc/source/doc")
    >>> path_1      = PPath("/Users/projetmbc/README")
    >>> path_2      = PPath("/Users/projetmbc/source/misTool/os_use.py")
    >>> path_danger = PPath("/NoUser/projects")
    >>> path.common_with(path_1)           # Same as ``path & path_1``
    PPath('/Users/projetmbc')
    >>> path.common_with(path_2)           # Same as ``path & path_2``
    PPath('/Users/projetmbc/source')
    >>> path.common_with(path_danger)      # No error raised !
    PPath('/')
    >>> path.common_with(path_1, path_2)   # Same as ``path & path_1 & path_2``
    PPath('/Users/projetmbc')
    >>> path.common_with([path_1, path_2]) # Same as ``path & [path_1, path_2]``
    PPath('/Users/projetmbc')

The class ``os_use.PPath`` adds a magic method so as to use
``path - anotherpath`` instead of ``path.relative_to(anotherpath)``
where the method ``relative_to`` is implemented by the class
``pathlib.Path``.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> main    = PPath("/Users/projetmbc")
    >>> path_1  = PPath("/Users/projetmbc/README")
    >>> path_2  = PPath("/Users/projetmbc/source/misTool/os_use.py")
    >>> path_1 - main
    PPath('README')
    >>> path_2 - main
    PPath('source/misTool/os_use.py')
    >>> path_2 - path_1
    Traceback (most recent call last):
    [...]
    ValueError: '/Users/projetmbc/source/misTool/os_use.py' does not start with '/Users/projetmbc/README'

If you need to know the depth of one path relatively to another, just
call the method ``depth_in``.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> main    = PPath("/Users/projetmbc")
    >>> path_1  = PPath("/Users/projetmbc/README")
    >>> path_2  = PPath("/Users/projetmbc/source/misTool/os_use.py")
    >>> path_pb = PPath("/NoUser/projects")
    >>> print(path_1.depth_in(main))
    0
    >>> print(path_2.depth_in(main))
    2
    >>> print(path_pb.depth_in(main))
    Traceback (most recent call last):
    [...]
    ValueError: '/NoUser/projects' does not start with '/Users/projetmbc'

The special concept of "regpath"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A "regpath" is a query mixing all the power of regexes and the Unix-glob
special characters *(there are also some additional query features)*. We
will use some "regpaths" in the incoming examples.

**See the docstring of the method ``regpath2meta`` for complete
informations about the "regpaths".**

Walk and see
~~~~~~~~~~~~

The method ``see`` **tries** to open the current path with a possible
associated application. For example, an HTML file will be opened by your
default browser.

You can walk very easily inside a directory thanks to the method
``walk`` and the "regpaths" *(see the previous section)*. For example,
let's suppose that we have the following directory with absolute path
``/Users/projetmbc/basic_dir`` in a Unix system.

::

    + basic_dir
        * latex_1.tex
        * latex_2.tex
        * python_1.py
        * python_2.py
        * python_3.py
        * python_4.py
        * text_1.txt
        * text_2.txt
        * text_3.txt
        + empty_dir
        + sub_dir
            * code_A.py
            * code_B.py
            * slide_A.pdf
            * slide_B.pdf
            + sub_sub_dir
                * doc.pdf

Here are easy to understand examples where the regpath ``"*"`` is for a
non-recursive search contrary to the regpath ``"**"``.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> folder = PPath("/Users/projetmbc/basic_dir")
    >>> for p in folder.walk("dir::**"):
    ...     print("+", p)
    ...
    + /Users/projetmbc/basic_dir/empty_dir
    + /Users/projetmbc/basic_dir/sub_dir
    + /Users/projetmbc/basic_dir/sub_dir/sub_sub_dir
    >>> for p in folder.walk("file::**.py"):
    ...     print("+", p)
    ...
    + /Users/projetmbc/basic_dir/python_1.py
    + /Users/projetmbc/basic_dir/python_2.py
    + /Users/projetmbc/basic_dir/python_3.py
    + /Users/projetmbc/basic_dir/python_4.py
    + /Users/projetmbc/basic_dir/sub_dir/code_A.py
    + /Users/projetmbc/basic_dir/sub_dir/code_B.py
    >>> for p in folder.walk("file::*.py"):
    ...     print("+", p)
    ...
    + /Users/projetmbc/basic_dir/python_1.py
    + /Users/projetmbc/basic_dir/python_2.py
    + /Users/projetmbc/basic_dir/python_3.py
    + /Users/projetmbc/basic_dir/python_4.py

Create
~~~~~~

Creating files and folders is straight forward with the method
``create`` even if this needs to add several parent directories that
don't yet exist. In the following example, we suppose that the current
directory has absolute path ``/Users/projetmbc``, and doesn't contain
any subfolder.

.. code:: python

    >>> from mistool.os_use import PPath
    >>> path_1 = PPath("test/README")
    >>> path_1.is_file()
    False
    >>> path_1.create("file")
    >>> path_1.is_file()
    True
    >>> path_2 = PPath("test/README")
    >>> path_2.create("dir")
    Traceback (most recent call last):
    [...]
    ValueError: path points to an existing file.

Remove
~~~~~~

If you want to destroy a whole directory, or simply a file, given by its
``PPath``, just use the method ``remove``.

**Warning ! Because removing a file or a directory can be a dangerous
thing, you can use the method ``can_be_removed`` which by default will
raise an OS error if the ``PPath`` is one of an existing file or
folder.**

The method ``clean`` allows to remove specific files and/or directories
matching a regpath given as an argument.

Move & copy
~~~~~~~~~~~

By default, the method ``copy_to`` allows you to copy a file or a
directory into another location, whereas the method ``move_to`` will
move a file or a directory to another place.

The module ``string_use``
=========================

Multi-replacements
------------------

The class ``string_use.MultiReplace`` makes possible to do
multi-replacements recursively or not *(by default
``mode = "norecu"``)*.

.. code:: python

    >>> from mistool.string_use import MultiReplace
    >>> from mistool.config.pattern import PATTERNS_WORDS
    >>> oldnew = {
    ...     'W1': "Word #1",
    ...     'W2': "Word #2",
    ...     'W3': "W1 and W2"
    ... }
    >>> mreplace = MultiReplace(
    ...     oldnew  = oldnew,
    ...     mode    = "recu",
    ...     pattern = PATTERNS_WORDS['var']
    ... )
    >>> print(mreplace("W1 and W2 = W3"))
    Word #1 and Word #2 = Word #1 and Word #2
    >>> mreplace.mode = "norecu"  
    >>> mreplace.build()
    >>> print(mreplace("W1 and W2 = W3"))
    Word #1 and Word #2 = W1 and W2

The code above show that cyclic definitions will raise a ``ValueError``
exception.

.. code:: python

    >>> from mistool.string_use import MultiReplace
    >>> from mistool.config.pattern import PATTERNS_WORDS
    >>> oldnew = {
    ...     'WRONG_1': "one small text and  WRONG_2",
    ...     'WRONG_2': "one small text, and then WRONG_3",
    ...     'WRONG_3': "with WRONG_1, there is one problem here"
    ... }
    >>> mreplace = MultiReplace(
    ...     oldnew  = oldnew,
    ...     mode    = "recu",
    ...     pattern = PATTERNS_WORDS["var"]
    ... )
    Traceback (most recent call last):
    [...]
    ValueError: the following viscious circle has been found.
         + WRONG_2 --> WRONG_3 --> WRONG_1 --> WRONG_2

Multi-splits
------------

The aim of the class ``string_use.MultiSplit`` is to split a text on
several semantic depths. Here is an example of use.

.. code:: python

    >>> from mistool.string_use import MultiSplit
    >>> msplit = MultiSplit(seps = "|")
    >>> print(msplit("p_1 ; p_2 ; p_3 | r_1 ; r_2 | s"))
    [
        'p_1 ; p_2 ; p_3 ',
        ' r_1 ; r_2 ',
        ' s'
    ]
    >>> msplit.seps  = ["|", ";"]
    >>> msplit.strip = True
    >>> print(msplit("p_1 ; p_2 ; p_3 | r_1 ; r_2 | s"))
    [
        ['p_1', 'p_2', 'p_3'],
        ['r_1', 'r_2'],
        ['s']
    ]

Before, between and after
-------------------------

The function ``string_use.between`` looks for two separators such as to
return the text before, between and after the first matching of this
separators. ``None`` is returned if no matching has been found. Just
take a look at a concrete example.

.. code:: python

    >>> from mistool.string_use import between
    >>> text = "f(x ; y) = x**2 + y**2"
    >>> seps = ["(", ")"]
    >>> print(between(text, seps))
    [
        'f',                # Before
        'x ; y',            # Between
        ' = x**2 + y**2'    # After
    ]
    >>> seps = ["{", "}"]
    >>> print(between(text, seps))
    None

Join with a last special text
-----------------------------

You can join several strings with a special final separator as the
examples above show.

.. code:: python

    >>> from mistool.string_use import joinand
    >>> texts = ["1", "2", "3"]
    >>> print(joinand(texts))
    1, 2 and 3
    >>> print(joinand(texts = texts, andtext = "et"))
    1, 2 et 3
    >>> print(joinand(texts = texts, sep = " + ", andtext = "="))
    1 + 2 = 3

Playing with cases of letters
-----------------------------

The function ``string_use.case`` gives more auto-formatting of strings
*(the last formatting looks strange but it is useful for an incoming
project of the author of ``mistool``)*.

.. code:: python

    >>> from mistool.string_use import case
    >>> text = "onE eXamPLe"
    >>> for kind in ['lower', 'upper', 'sentence', 'title', 'firstlast']:
    ...     print("{0}  [{1}]".format(case(text, kind), kind))
    ...
    one example   [lower]
    ONE EXAMPLE   [upper]
    One example   [sentence]
    One Example   [title]
    One examplE   [firstlast]

A camel case string can be "uncamelized" by the function
``string_use.camelto``. Here is how to use it *(you can change the
separator by using the optional argument ``sep`` which is ``"_"`` by
default)*.

.. code:: python

    >>> from mistool.string_use import camelto
    >>> text = "OneSmallExampLE"
    >>> for kind in ['lower', 'upper', 'sentence', 'title', 'firstlast']:
    ...     print("{0}  [{1}]".format(camelto(text, kind), kind))
    ...
    one_small_examp_l_e   [lower]
    ONE_SMALL_EXAMP_L_E   [upper]
    One_small_examp_l_e   [sentence]
    One_Small_Examp_L_E   [title]
    One_small_examp_l_E   [firstlast]

If you need to check the case of a string, just use
``string_use.iscase(text, kind)``.

Playing with ASCII
------------------

You can check if a string is a pure ASCII one.

.. code:: python

    >>> from mistool.string_use import isascii
    >>> print(isascii("Vive la France !"))
    True
    >>> print(isascii("¡Viva España!"))
    False

You can also transform a string to a pure ASCII one *(this will not
always work but in case of failure you can contribute very easily to
enhance ``string_use.asciify``)*.

.. code:: python

    >>> from mistool.string_use import asciify
    >>> print(asciify("¡Viva España!"))
    Viva Espana!
    >>> oldnew = {'!': ""}
    >>> print(asciify(text = "¡Viva España!", oldnew = oldnew))
    Viva Espana

The last example above shows how to be permissive : this means that
``string_use.asciify`` will "asciify" the most characters as possible.

.. code:: python

    >>> from mistool.string_use import asciify
    >>> print(asciify(text = "L'Odyssée de ∏", strict = False))
    L'Odyssee de ∏
    >>> print(asciify("L'Odyssée de ∏"))
    Traceback (most recent call last):
    [...]
    ValueError: ASCII conversion can't be made because of the character << ∏ >>.
    You can use the function ``_ascii_report`` so as to report more precisely
    this fealure with eventually an ascii alternative.

Auto completion
---------------

The class ``string_use.AutoComplete`` gives the auto-completion feature
accessible without using any GUI package.

.. code:: python

    >>> from mistool.string_use import AutoComplete
    >>> myac = AutoComplete(
    ...     words = [
    ...         "article", "artist", "art",
    ...         "when", "who", "whendy",
    ...         "bar", "barbie", "barber", "bar"
    ...     ]
    ... )
    >>> print(myac.matching("art"))
    ['article', 'artist']
    >>> print(myac.matching(""))
    [
        'art', 'article', 'artist',
        'bar', 'barber', 'barbie',
        'when', 'whendy', 'who'
    ]
    >>> print(myac.missing("art", 'article'))
    icle

It is a convention in GUI applications to give auto-completion only for
at least three characters. You can do that by using the optional
argument ``minsize`` which is ``1`` by default.

The module ``term_use``
=======================

Auto-numbering steps
--------------------

For terminal informations, it can be useful to number some important
printed steps. This can be done easily with the class ``term_use.Step``.

.. code:: python

    >>> from mistool.term_use import Step
    >>> mysteps = Step()
    >>> i = 0
    >>> while i <= 12:
    ...     if i % 2:
    ...         mysteps("Action #{0}".format(i))
    ...     i += 1
    ...
    1) Action #1
    2) Action #3
    3) Action #5
    4) Action #7
    5) Action #9
    6) Action #11

The class ``term_use.Step`` has two optional arguments.

1. ``start`` gives the first number which is ``1`` by default.

2. ``textit`` is a function of two variables ``(n, t)`` returning the
   text containing the step number ``n`` and the text ``t``. By default,
   ``textit = lambda n, t: "{0}) {1}".format(n, t)``.

Frame
-----

The function ``term_use.withframe`` puts a text inside an ASCII frame
*(you can choose the alignment and use other kinds of frames if
necessary as it is explained in the docstrings)*.

.. code:: python

    >>> from mistool.term_use import withframe
    >>> text = '''
    ... One small
    ... text
    ... to do tests
    ... '''.strip()
    >>> print(withframe(text))
    ###############
    # One small   #
    # text        #
    # to do tests #
    ###############

ASCII tree views of one directory
---------------------------------

For our examples, we consider a folder with the following structure and
the absolute path ``/Users/projetmbc/dir``.

::

    + dir
        * code_1.py
        * code_2.py
        * file_1.txt
        * file_2.txt
        + doc
            * code_A.py
            * code_B.py
            * slide_A.pdf
            * slide_B.pdf
            + licence
                * doc.pdf
        + emptydir

The preceding ASCII tree view was built easily using the following code
*(``PPath`` is the class defined in ``os_use`` added in ``term_use`` for
you comfort)*.

.. code:: python

    >>> from mistool.term_use import DirView, PPath
    >>> dir     = PPath("/Users/projetmbc/dir")
    >>> dirview = DirView(
    ...     ppath   = dir,
    ...     sorting = "filefirst"
    ... )
    >>> print(dirview.ascii)
    + dir
        * code_1.py
        * code_2.py
        * file_1.txt
        * file_2.txt
        + doc
            * code_A.py
            * code_B.py
            * slide_A.pdf
            * slide_B.pdf
            + licence
                * doc.pdf
        + emptydir

Using the "regpath" concept of the module ``os_use``, we can filter
folders and files shown as in the example above *(we also use the
argument ``display`` so as to customize the output)*.

.. code:: python

    >>> from mistool.term_use import DirView, PPath
    >>> dir     = PPath("/Users/projetmbc/dir")
    >>> dirview = DirView(
    ...     ppath   = dir,
    ...     regpath = "file::**.py",
    ...     display = "main short found"
    ... )
    >>> print(dirview.ascii)
    + dir
        * code_1.py
        * code_2.py
        + doc
            * code_A.py
            * code_B.py

You can also use the following property methods.

1. ``dirview.tree`` is a graphical tree.

2. ``dirview.toc`` gives a minimal tabulated tree.

3. ``dirview.latex`` is for the LaTeX package ``dirtree``.

The module ``python_use``
=========================

A multikeys dictionary
----------------------

The class ``MKOrderedDict`` allows to work easily with multikeys ordered
dictionaries. Here is a complete example of use.

.. code:: python

    >>> from mistool.python_use import MKOrderedDict
    >>> onemkdict = MKOrderedDict()
    >>> onemkdict[(1, 2, 4)] = "1st value"
    >>> onemkdict["key"] = "2nd value"
    >>> onemkdict["key"] = "3rd value"
    >>> print(onemkdict)
    MKOrderedDict([
        ((id=0, key=(1, 2, 4)), value='1st value'),
        ((id=0, key='key')    , value='2nd value'),
        ((id=1, key='key')    , value='3rd value')
    ])
    >>> for k_id, val in onemkdict["key"]:
    ...     print(k_id, val)
    ...
    0 2nd value
    1 3rd value
    >>> print(onemkdict.getitembyid(1, "key"))
    3rd value
    >>> for (k_id, key), val in onemkdict.items():
    ...     print((k_id, key), "===>", val)
    ...
    (0, (1, 2, 4)) ===> 1st value
    (0, 'key') ===> 2nd value
    (1, 'key') ===> 3rd value
    >>> for key, val in onemkdict.items(noid=True):
    ...     print(key, "===>", val)
    ...
    (1, 2, 4) ===> 1st value
    key ===> 2nd value
    key ===> 3rd value
    >>> "key" in onemkdict
    True
    >>> "kaaaay" in onemkdict
    False
    >>> onemkdict.setitembyid(0, "key", "New 2nd value")
    >>> print(onemkdict)
    MKOrderedDict([
        ((id=0, key=(1, 2, 4)), value='1st value'),
        ((id=0, key='key')    , value='New 2nd value'),
        ((id=1, key='key')    , value='3rd value')])

A dictionary defined recursively
--------------------------------

The class ``RecuOrderedDict`` allows to use a list of hashable keys, or
just a single hashable key. Here is a complete example of use.

.. code:: python

    >>> from mistool.python_use import RecuOrderedDict
    >>> onerecudict = RecuOrderedDict()
    >>> onerecudict[[1, 2, 4]] = "1st value"
    >>> onerecudict[(1, 2, 4)] = "2nd value"
    >>> onerecudict["key"] = "3rd value"
    >>> print(onerecudict)
    RecuOrderedDict([
        (
            1,
            RecuOrderedDict([
                (
                    2,
                    RecuOrderedDict([ (4, '1st value') ])
                )
            ])
        ),
        (
            (1, 2, 4),
            '2nd value'
        ),
        (
            'key',
            '3rd value'
        )
    ])
    >>> [1, 2, 4] in onerecudict
    True
    >>> [2, 4] in onerecudict[1]
    True

List of single values of a dictionary
-------------------------------------

If you need to list all the value of one dictionary, the function
``python_use.dictvalues`` is made for you.

.. code:: python

    >>> from mistool.python_use import dictvalues
    >>> onedict = {"a": 1, "b": 2, "c": 1}
    >>> print(dictvalues(onedict))
    [1, 2]
    >>> print(list(onedict.values()))
    [2, 1, 1]

Easy quoted text with the least escaped quote symbols
-----------------------------------------------------

With ``python_use.quote`` you can add without pain quotes around a text.

.. code:: python

    >>> from mistool.python_use import quote
    >>> print(quote('First example.'))
    'First example.'
    >>> print(quote("Same example."))
    'Same example.'
    >>> print(quote('One "small" example.'))
    'One "small" example.'
    >>> print(quote("Another kind of \"example\"."))
    'Another kind of "example".'
    >>> print(quote("An example a 'little' more \"problematic\"."))
    'An example a \'little\' more "problematic".'

The module ``date_use``
=======================

Translating dates
-----------------

The function ``date_use.translate`` translates safely and easily all the
names in dates.

.. code:: python

    >>> import datetime
    >>> from mistool.date_use import translate
    >>> onedate   = datetime.date(2015, 6, 2)
    >>> oneformat = "%A %d %B %Y"
    >>> print(translate(date = onedate, strformat = oneformat))
    Tuesday 02 June 2015
    >>> print(translate(date = onedate, strformat = oneformat, lang = "fr_FR"))
    Mardi 02 juin 2015

Next day having a fixed english name
------------------------------------

In some applications you want to know the next monday after a fixing
date. Here is how to do that.

.. code:: python

    >>> from datetime import datetime
    >>> from mistool.date_use import nextday
    >>> onedate = datetime.strptime("2013-11-30", "%Y-%m-%d")
    >>> print(onedate.strftime("%Y-%m-%d is a %A"))
    2013-11-30 is a Saturday
    >>> nextsunday = nextday(date = onedate, name = "sunday")
    >>> print("Next Sunday:", nextsunday.strftime("%Y-%m-%d"))
    Next Sunday: 2013-12-01

The module ``url_use``
======================

Looking for dead or bad urls
----------------------------

For the following example, we suppose that we have a working internet
connection.

.. code:: python

    >>> from mistool.url_use import islinked
    >>> islinked("http://www.google.com")
    True
    >>> islinked("http://www.g-o-o-g-l-e.com")
    False

Escaping special characters in urls
-----------------------------------

It is safe to not use non-ASCII characters in a url. Here is one way to
do that.

.. code:: python

    >>> from mistool.url_use import escape
    >>> print(escape("http://www.vivaespaña.com/camión/"))
    http://www.vivaespa%C3%B1a.com/cami%C3%B3n/

The module ``latex_use``
========================

Escaping the special LaTeX characters
-------------------------------------

The function ``latex_use.escape`` will escape all special characters for
you regarding the text or math mode.

.. code:: python

    >>> from mistool.latex_use import escape
    >>> onetext = "\OH/ & ..."
    >>> print(escape(onetext))
    \textbackslash{}OH/ \& ...
    >>> print(escape(text = onetext, mode = "math"))
    \backslash{}OH/ \& ...

Easy LaTeX compilation(s)
-------------------------

The class ``latex_use.Build`` compiles a LaTeX file for you *(for the
moment only the PDF compilation is implemented)*. Let's consider the
following LaTeX file with the absolute path
``/Users/projetmbc/latex/file.tex``.

.. code:: latex

    \documentclass[11pt, oneside]{article}

    \begin{document}

    \section{One little test}

    One basic formula : $E = mc^2$.

    \end{document}

In the following code, we call to the class ``term_use.DirView`` so as
to show the new files made by LaTeX *(the ellipsis ``[...]`` indicates
some lines not reproduced here)*.

.. code:: python

    >>> from mistool.latex_use import Build, PPath
    >>> from mistool.term_use import DirView
    >>> latexdir = PPath("/Users/projetmbc/latex/file.tex")
    >>> print(DirView(latexdir.parent).ascii)
    + latex
        * file.tex
    >>> builder   = Build(latexdir)
    >>> builder.pdf()
    # -- Start of compilation Nb.1 -- #

    This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) (preloaded
    format=pdflatex)
     restricted \write18 enabled.
    entering extended mode

    [...]

    Output written on file.pdf (1 page, 36666 bytes).
    Transcript written on file.log.

    # -- End of compilation Nb.1 -- #
    >>> print(DirView(latexdir.parent).ascii)
    + latex
        * file.aux
        * file.log
        * file.pdf
        * file.tex

The PDF file has been build by LaTeX but there are also temporary ones.
If you need several compilations, so as to build a table of content for
example, just use the attribut-argument ``repeat``, and if you don't
want to see the LaTeX ouput, just set the attribut-argument
``showinfos`` to ``False``.

Removing the temporary files produced by LaTeX
----------------------------------------------

We keep the same LaTeX example file. The function ``latex_use.clean``
cleans all unuseful temporary files when the compilation has been done.

.. code:: python

    >>> from mistool.latex_use import clean, PPath
    >>> from mistool.term_use import DirView
    >>> latexdir = PPath("/Users/projetmbc/latex")
    >>> print(DirView(latexdir.parent).ascii)
    + latex
        * file.aux
        * file.log
        * file.pdf
        * file.synctex.gz
        * file.tex
    >>> clean(ppath = latexdir, showinfos = True)
    * Cleaning for "/Users/projetmbc/latex/file.tex"
    >>> print(DirView(latexdir.parent).ascii)
    + latex
        * file.pdf
        * file.tex

Automatic installation of personal LaTeX packages
-------------------------------------------------

Let's suppose that we have package named ``lyxam`` stored in a folder
having the absolute path ``/Users/projetmbc/latex/lyxam`` and whose
structure is the following one.

::

    + lyxam
        + change_log
            + 2012
                * 02.txt
                * 03.txt
                * 04.txt
                * 10.txt
            * todo.txt
        * lyxam.sty
        + config
            * settings.tex
            + lang
                * en.tex
                * fr.tex
                + special
                    * fr.config
                + standard
                    * en.config
                    * fr.config
            + style
                * apmep.tex
                * default.tex

To install this package locally in your LaTeX distribution, just do like
in the code above.

.. code:: python

    >>> from mistool.latex_use import install, PPath
    >>> package = PPath("/Users/projetmbc/latex/lyxam")
    >>> install(package)
    Starting installation of the package locally.
        * Deletion of the old << lyxam >> package in the local LaTeX directory.
        * Creation of a new << lyxam >> package in the local LaTeX directory.
            + Adding the new file << lyxam.sty >>
            + Adding the new file << change_log/todo.txt >>
            + Adding the new file << change_log/2012/02.txt >>
            + Adding the new file << change_log/2012/03.txt >>
            + Adding the new file << change_log/2012/04.txt >>
            + Adding the new file << change_log/2012/10.txt >>
            + Adding the new file << config/settings.tex >>
            + Adding the new file << config/lang/en.tex >>
            + Adding the new file << config/lang/fr.tex >>
            + Adding the new file << config/lang/special/fr.config >>
            + Adding the new file << config/lang/standard/en.config >>
            + Adding the new file << config/lang/standard/fr.config >>
            + Adding the new file << config/style/apmep.tex >>
            + Adding the new file << config/style/default.tex >>
        * Refreshing the list of LaTeX packages.

Using the concept of "regpath" of the module ``os_use``, you can for
example choose to not install all the ``TXT`` files.

.. code:: python

    >>> from mistool.latex_use import install, PPath
    >>> package = PPath("/Users/projetmbc/latex/lyxam")
    >>> install(ppath = package, regpath = "file not::**.txt")
    Starting installation of the package locally.
        * Deletion of the old << lyxam >> package in the local LaTeX directory.
        * Creation of a new << lyxam >> package in the local LaTeX directory.
            + Adding the new file << lyxam.sty >>
            + Adding the new file << config/settings.tex >>
            + Adding the new file << config/lang/en.tex >>
            + Adding the new file << config/lang/fr.tex >>
            + Adding the new file << config/lang/special/fr.config >>
            + Adding the new file << config/lang/standard/en.config >>
            + Adding the new file << config/lang/standard/fr.config >>
            + Adding the new file << config/style/apmep.tex >>
            + Adding the new file << config/style/default.tex >>
        * Refreshing the list of LaTeX packages.

Remove a personal LaTeX packages
--------------------------------

Just use ``remove(name)`` where ``name`` is the name of a local LaTeX
package.


