Compare commits
2 Commits
e9b4bfb990
...
pkg/dev/ri
Author | SHA1 | Date | |
---|---|---|---|
58b3880e9b | |||
413ab6dbb0 |
27
README.md
27
README.md
@ -1,27 +0,0 @@
|
|||||||
Rougail
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
[Documentation (french)](doc/README.md)
|
|
662
debian/agpl-3
vendored
Normal file
662
debian/agpl-3
vendored
Normal file
@ -0,0 +1,662 @@
|
|||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
11
|
14
debian/control
vendored
Normal file
14
debian/control
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Source: rougail
|
||||||
|
Section: admin
|
||||||
|
Priority: extra
|
||||||
|
Maintainer: Cadoles <contact@cadoles.com>
|
||||||
|
Build-depends: debhelper (>=11), python3-all, python3-setuptools
|
||||||
|
Standards-Version: 3.9.4
|
||||||
|
Homepage: https://forge.cadoles.com/Infra/rougail
|
||||||
|
|
||||||
|
Package: rougail
|
||||||
|
Architecture: any
|
||||||
|
Pre-Depends: dpkg, python3, ${misc:Pre-Depends}
|
||||||
|
Depends: ${python:Depends}, ${misc:Depends}
|
||||||
|
Description: configuration manager
|
||||||
|
|
22
debian/copyright
vendored
Normal file
22
debian/copyright
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: risotto
|
||||||
|
Upstream-Contact: Cadoles <contact@cadoles.com>
|
||||||
|
Source: https://forge.cadoles.com/Infra/rougail
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2019-2020 Cadoles <contact@cadoles.com>
|
||||||
|
License: AGPL-3+
|
||||||
|
|
||||||
|
License: AGPL-3+
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
3
debian/rougail.dirs
vendored
Normal file
3
debian/rougail.dirs
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/var/rougail/patches
|
||||||
|
/var/rougail/templates
|
||||||
|
/var/rougail/manifests
|
10
debian/rules
vendored
Normal file
10
debian/rules
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
# See debhelper(7) (uncomment to enable)
|
||||||
|
# output every command that modifies files on the build system.
|
||||||
|
#DH_VERBOSE = 1
|
||||||
|
|
||||||
|
export PYBUILD_NAME = rougail
|
||||||
|
export PYBUILD_DISABLE_python3 = test
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@ --with python3 --buildsystem=pybuild
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.0 (quilt)
|
@ -1,21 +0,0 @@
|
|||||||
# Rougail
|
|
||||||
|
|
||||||
## Les variables
|
|
||||||
|
|
||||||
- [Les familles](family/README.md)
|
|
||||||
- [Les variables](variable/README.md)
|
|
||||||
|
|
||||||
## Les services
|
|
||||||
|
|
||||||
- [Les services](service/README.md)
|
|
||||||
- [La gestion d'un service](service/service.md)
|
|
||||||
- [La gestion d'un fichier](service/file.md)
|
|
||||||
- [La gestion d'un fichier de service systemd](service/override.md)
|
|
||||||
- [La gestion d'un port](service/port.md)
|
|
||||||
- [La gestion d'une ip](service/ip.md)
|
|
||||||
|
|
||||||
## Les contraintes
|
|
||||||
|
|
||||||
- [Les calcules automatiques](fill/README.md)
|
|
||||||
- [Les vérifications des valeurs](check/README.md)
|
|
||||||
- [Les conditions](condition/README.md)
|
|
@ -1,10 +0,0 @@
|
|||||||
# Les vérifications des valeurs
|
|
||||||
|
|
||||||
- [Fonction de vérification](function.md)
|
|
||||||
- [Cible de la fonction](../target/only_var.md)
|
|
||||||
- [Paramètre de la fonction](../param/README.md)
|
|
||||||
- [Réfinition](redefine.md)
|
|
||||||
|
|
||||||
FIXME
|
|
||||||
<!ATTLIST variable remove_check (True|False) "False">
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
|||||||
# Valeur calculée de la variable
|
|
||||||
|
|
||||||
## Variable avec une valeur par défaut calculée
|
|
||||||
|
|
||||||
Créons une variable dont la valeur est retournée par la fonction "return_no" :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="my_calculated_variable"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_no">
|
|
||||||
<target>my_calculated_variable</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Puis créons la fonction "return_no" :
|
|
||||||
|
|
||||||
```
|
|
||||||
def return_no():
|
|
||||||
return 'no'
|
|
||||||
```
|
|
||||||
|
|
||||||
La [cible](../target/variable.md) du calcul est ici "my_calculated_variable".
|
|
||||||
|
|
||||||
Dans ce cas, la valeur par défaut est la valeur retournée par la fonction (ici "no"), elle sera calculée tant que l'utilisateur n'a pas de spécifié une valeur à cette variable.
|
|
||||||
|
|
||||||
Attention, si une valeur par défaut est définit dans la variable "my_calculated_variable" :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_calculated_variable">
|
|
||||||
<value>yes</value>
|
|
||||||
</variable>
|
|
||||||
```
|
|
||||||
Cette valeur par défaut sera complètement ignorée. C'est le calcul qui en définira la valeur.
|
|
||||||
|
|
||||||
Il est possible de définir des [paramètres](../param/README.md) à cette fonction.
|
|
||||||
|
|
||||||
## Variable avec une valeur calculée
|
|
||||||
|
|
||||||
En ajoutant le paramètre "hidden" à "True" dans la variable précédente, l'utilisateur n'aura plus la possibilité de modifié la valeur. La valeur de la variable sera donc systématiquement calculée :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_calculated_variable" hidden="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Si une condition "hidden_if_in" est spécifié à la variable, la valeur sera modifiable par l'utilisateur si elle n'est pas cachée mais elle sera systèmatiquement calculée (même si elle a déjà était modifiée) si la variable est cachée.
|
|
||||||
|
|
||||||
## Variable meneuse ou suiveuse avec valeur calculé
|
|
||||||
|
|
||||||
Une [variable suiveuse](../variable/leadership.md) ne peut pas être calculé automatiquement.
|
|
||||||
Une [variable meneuse](../variable/leadership.md) peut être calculé automatiquement.
|
|
||||||
Si la variable n'est pas multiple, il ne faut pas que le calcule retourne une liste.
|
|
||||||
|
|
||||||
## Variable dynamique avec une valeur calculée
|
|
||||||
|
|
||||||
Il est également possible de calculer [une variable d'une famille dynamique](../family/auto.md) à partir d'une variable standard :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name='family'>
|
|
||||||
<variable name='suffixes' type='string' description="Suffixes of dynamic family" multi="True">
|
|
||||||
<value>val1</value>
|
|
||||||
<value>val2</value>
|
|
||||||
</variable>
|
|
||||||
<variable name="my_variable" type="string" description="My variable">
|
|
||||||
<value>val</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
<family name='dyn' dynamic="suffixes">
|
|
||||||
<variable name="my_calculated_variable_dyn_" type="string" description="My calculated variable"/>
|
|
||||||
<value>val</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_value">
|
|
||||||
<param type="variable">my_variable</param>
|
|
||||||
<target>my_calculated_variable_dyn_</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas, les variables dynamiques "my_calculated_variable_dyn_" seront calculés à partir de la valeur de la variable "my_variable".
|
|
||||||
Que cela soit pour la variable "my_calculated_variable_dyn_val1" et "my_calculated_variable_dyn_val2".
|
|
||||||
|
|
||||||
Par contre, il n'est pas possible de faire un calcul pour une seule des deux variables issues de la variable dynamique.
|
|
||||||
Si c'est ce que vous cherchez à faire, il faudra prévoir un traitement particulier dans votre fonction.
|
|
||||||
|
|
||||||
Dans ce cas, il faut explicitement demander la valeur du suffix dans la fonction :
|
|
||||||
|
|
||||||
```
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_value_suffix">
|
|
||||||
<param type="variable">my_variable</param>
|
|
||||||
<param type="suffix"/>
|
|
||||||
<target>my_calculated_variable_dyn_</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Et ainsi faire un traitement spécifique pour ce suffix :
|
|
||||||
|
|
||||||
```
|
|
||||||
def return_value_suffix(value, suffix):
|
|
||||||
if suffix == 'val1':
|
|
||||||
return value
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable avec valeur calculée obligatoire
|
|
||||||
|
|
||||||
Par défaut les variables calculées ne sont pas des variables obligatoires.
|
|
||||||
Dans ce cas un calcul peut retourner "None" ou "", mais surtout un utilisateur peut spécifier une valeur nulle à cette variable. Dans ce cas le calcul ne sera plus réalisé.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!ELEMENT check (param*)>
|
|
||||||
<!ATTLIST check name CDATA #REQUIRED>
|
|
||||||
<!ATTLIST check level (error|warning) "error">
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
FIXME
|
|
||||||
<!ATTLIST variable remove_condition (True|False) "False">
|
|
||||||
|
|
||||||
|
|
||||||
<!ELEMENT condition ((target | param)+ )>
|
|
||||||
<!ATTLIST condition name (disabled_if_in|disabled_if_not_in|hidden_if_in|hidden_if_not_in|mandatory_if_in|mandatory_if_not_in) #REQUIRED>
|
|
||||||
<!ATTLIST condition source CDATA #REQUIRED>
|
|
||||||
<!ATTLIST condition fallback (True|False) "False">
|
|
||||||
<!ATTLIST condition force_condition_on_fallback (True|False) "False">
|
|
||||||
<!ATTLIST condition force_inverse_condition_on_fallback (True|False) "False">
|
|
@ -1,5 +0,0 @@
|
|||||||
# Famille
|
|
||||||
|
|
||||||
- [Une famille](simple.md)
|
|
||||||
- [Famille crée dynamiquement](auto.md)
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
|||||||
# Famille crée dynamiquement
|
|
||||||
|
|
||||||
Pour créer une famille dynamiquement, il faut créer une family fictive lié à une variable.
|
|
||||||
Le nom et la description de la famille et des variables qu'elle contient sera en réalité le prefix du nouveau nom/description. Le suffix viendra de la variable liée.
|
|
||||||
|
|
||||||
Par exemple :
|
|
||||||
|
|
||||||
```
|
|
||||||
<family name='my_family'>
|
|
||||||
<variable name='varname' multi="True">
|
|
||||||
<value>val1</value>
|
|
||||||
<value>val2</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
<family name='my_dyn_family_' dynamic="varname description="Describe "">
|
|
||||||
<variable name='my_dyn_var_'/>
|
|
||||||
</family>
|
|
||||||
```
|
|
||||||
|
|
||||||
Créera trois familles :
|
|
||||||
|
|
||||||
- la famille : "my_family"
|
|
||||||
- la famille dynamique : "my_dyn_family_val1" avec la description "Describe val1"
|
|
||||||
- la famille dynamique : "my_dyn_family_val2" avec la description "Describe val2"
|
|
||||||
|
|
||||||
Dans la famille dynamique "my_dyn_family_val1" on retrouvera une variable "my_dyn_var_val1".
|
|
||||||
|
|
||||||
Bien évidement si le contenu de "varname" venait a évolué, de nouvelles familles dynamiques pouvent apparaitre ou des familles dynamiques peuvent disparaître.
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
|||||||
# Une famille
|
|
||||||
|
|
||||||
Une famille est un [conteneur de variables](../variables.md).
|
|
||||||
|
|
||||||
Pour décrire une famille il faut mettre au minimum un nom :
|
|
||||||
|
|
||||||
```
|
|
||||||
<family name="my_family"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette famille doit être placé dans une balise [variables](../variables.md) :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="my_family"/>
|
|
||||||
</variables>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Description et aide de la famille
|
|
||||||
|
|
||||||
En plus d'un nom, il est possible de mettre une "description" à la famille. C'est une information "utilisateur" qui nous permettra d'avoir des informations complémentaires sur le contenu de cette famille :
|
|
||||||
|
|
||||||
```
|
|
||||||
<family name="my_family" description="This is a great family"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
En plus de la description, il est possible de préciser une aide complémentaire :
|
|
||||||
|
|
||||||
```
|
|
||||||
<family name="my_family" help="This is a great family"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mode de la famille
|
|
||||||
|
|
||||||
Le [mode](../mode.md) par défaut d'une famille correspond au [mode](../mode.md) le plus petite des variables dans cette famille.
|
|
||||||
|
|
||||||
Changer le [mode](../mode.md) d'une famille permet de définir le [mode](../mode.md) par défaut des variables inclusent dans cette famille.
|
|
||||||
|
|
||||||
Pour définir le [mode](../mode.md) :
|
|
||||||
|
|
||||||
```
|
|
||||||
<family name="my_family" mode="expert"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Famille invisible
|
|
||||||
|
|
||||||
Il est possible de cacher une famille, ainsi toutes les variables inclusent dans cette famille.
|
|
||||||
|
|
||||||
Cacher une famille signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service.
|
|
||||||
Par contre ces variables sont accessibles lorsqu'on va utiliser ces variables.
|
|
||||||
|
|
||||||
Pour cacher une famille :
|
|
||||||
|
|
||||||
```
|
|
||||||
<family name="my_family" hidden="True"/>
|
|
||||||
```
|
|
@ -1,8 +0,0 @@
|
|||||||
# Les variables calculées
|
|
||||||
|
|
||||||
Une variable calculée est une variable donc sa valeur est le résultat d'une fonction python.
|
|
||||||
|
|
||||||
- [Valeur calculée de la variable](value.md)
|
|
||||||
- [Cible de la fonction](../target/only_var.md)
|
|
||||||
- [Paramètre de la fonction](../param/README.md)
|
|
||||||
- [Réfinition](redefine.md)
|
|
@ -1,66 +0,0 @@
|
|||||||
# Rédéfinition
|
|
||||||
|
|
||||||
## Redéfinition des calcules
|
|
||||||
|
|
||||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="my_calculated_variable"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_no">
|
|
||||||
<target>my_calculated_variable</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans un second dictionnaire il est possible de redéfinir le calcul :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="my_calculated_variable" redefine="True"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_yes">
|
|
||||||
<target>my_calculated_variable</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas, à aucun moment la fonction "return_no" ne sera exécuté. Seul la fonction "return_yes" le sera.
|
|
||||||
|
|
||||||
## Redéfinition avec suppression d'un calcul
|
|
||||||
|
|
||||||
Il se peut que dans un dictionnaire on décide de définir une valeur par défaut à une variable via un calcul.
|
|
||||||
|
|
||||||
Dans un second dictionnaire il est possible de supprimer ce calcul.
|
|
||||||
|
|
||||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="my_calculated_variable"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_no">
|
|
||||||
<target>my_calculated_variable"</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans un second dictionnaire supprimer ce calcul :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="my_calculated_variable" redefine="True" remove_fill="True"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
```
|
|
@ -1,117 +0,0 @@
|
|||||||
# Valeur calculée de la variable
|
|
||||||
|
|
||||||
## Variable avec une valeur par défaut calculée
|
|
||||||
|
|
||||||
Créons une variable dont la valeur est retournée par la fonction "return_no" :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="my_calculated_variable"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_no">
|
|
||||||
<target>my_calculated_variable</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Puis créons la fonction "return_no" :
|
|
||||||
|
|
||||||
```
|
|
||||||
def return_no():
|
|
||||||
return 'no'
|
|
||||||
```
|
|
||||||
|
|
||||||
La [cible](../target/variable.md) du calcul est ici "my_calculated_variable".
|
|
||||||
|
|
||||||
Dans ce cas, la valeur par défaut est la valeur retournée par la fonction (ici "no"), elle sera calculée tant que l'utilisateur n'a pas de spécifié une valeur à cette variable.
|
|
||||||
|
|
||||||
Attention, si une valeur par défaut est définit dans la variable "my_calculated_variable" :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_calculated_variable">
|
|
||||||
<value>yes</value>
|
|
||||||
</variable>
|
|
||||||
```
|
|
||||||
Cette valeur par défaut sera complètement ignorée. C'est le calcul qui en définira la valeur.
|
|
||||||
|
|
||||||
Il est possible de définir des [paramètres](../param/README.md) à cette fonction.
|
|
||||||
|
|
||||||
## Variable avec une valeur calculée
|
|
||||||
|
|
||||||
En ajoutant le paramètre "hidden" à "True" dans la variable précédente, l'utilisateur n'aura plus la possibilité de modifié la valeur. La valeur de la variable sera donc systématiquement calculée :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_calculated_variable" hidden="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Si une condition "hidden_if_in" est spécifié à la variable, la valeur sera modifiable par l'utilisateur si elle n'est pas cachée mais elle sera systèmatiquement calculée (même si elle a déjà était modifiée) si la variable est cachée.
|
|
||||||
|
|
||||||
## Variable meneuse ou suiveuse avec valeur calculé
|
|
||||||
|
|
||||||
Une [variable suiveuse](../variable/leadership.md) ne peut pas être calculé automatiquement.
|
|
||||||
Une [variable meneuse](../variable/leadership.md) peut être calculé automatiquement.
|
|
||||||
Si la variable n'est pas multiple, il ne faut pas que le calcule retourne une liste.
|
|
||||||
|
|
||||||
## Variable dynamique avec une valeur calculée
|
|
||||||
|
|
||||||
Il est également possible de calculer [une variable d'une famille dynamique](../family/auto.md) à partir d'une variable standard :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name='family'>
|
|
||||||
<variable name='suffixes' type='string' description="Suffixes of dynamic family" multi="True">
|
|
||||||
<value>val1</value>
|
|
||||||
<value>val2</value>
|
|
||||||
</variable>
|
|
||||||
<variable name="my_variable" type="string" description="My variable">
|
|
||||||
<value>val</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
<family name='dyn' dynamic="suffixes">
|
|
||||||
<variable name="my_calculated_variable_dyn_" type="string" description="My calculated variable"/>
|
|
||||||
<value>val</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_value">
|
|
||||||
<param type="variable">my_variable</param>
|
|
||||||
<target>my_calculated_variable_dyn_</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas, les variables dynamiques "my_calculated_variable_dyn_" seront calculés à partir de la valeur de la variable "my_variable".
|
|
||||||
Que cela soit pour la variable "my_calculated_variable_dyn_val1" et "my_calculated_variable_dyn_val2".
|
|
||||||
|
|
||||||
Par contre, il n'est pas possible de faire un calcul pour une seule des deux variables issues de la variable dynamique.
|
|
||||||
Si c'est ce que vous cherchez à faire, il faudra prévoir un traitement particulier dans votre fonction.
|
|
||||||
|
|
||||||
Dans ce cas, il faut explicitement demander la valeur du suffix dans la fonction :
|
|
||||||
|
|
||||||
```
|
|
||||||
<constraints>
|
|
||||||
<fill name="return_value_suffix">
|
|
||||||
<param type="variable">my_variable</param>
|
|
||||||
<param type="suffix"/>
|
|
||||||
<target>my_calculated_variable_dyn_</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Et ainsi faire un traitement spécifique pour ce suffix :
|
|
||||||
|
|
||||||
```
|
|
||||||
def return_value_suffix(value, suffix):
|
|
||||||
if suffix == 'val1':
|
|
||||||
return value
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable avec valeur calculée obligatoire
|
|
||||||
|
|
||||||
Par défaut les variables calculées ne sont pas des variables obligatoires.
|
|
||||||
Dans ce cas un calcul peut retourner "None" ou "", mais surtout un utilisateur peut spécifier une valeur nulle à cette variable. Dans ce cas le calcul ne sera plus réalisé.
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
Mode
|
|
||||||
====
|
|
||||||
|
|
||||||
Il existe trois "mode" dans Rougail :
|
|
||||||
|
|
||||||
- basic : variables indispensables à la mise en place d'un service
|
|
||||||
- normal : variables couramment modifié par l'utilisateur
|
|
||||||
- expert : variables a manipuler avec précausion et en toute connaissance de cause
|
|
@ -1,7 +0,0 @@
|
|||||||
# Paramètre de la fonction
|
|
||||||
|
|
||||||
- [Paramètre positionnel ou nommée](positional.md)
|
|
||||||
- [Type de paramètre simple](simple.md)
|
|
||||||
- [Type de paramètre "variable"](variable.md)
|
|
||||||
- [Type de paramètre "information"](information.md)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
# Paramètre de type information
|
|
||||||
|
|
||||||
Le paramètre peut être la valeur est issue d'une information de la configuration.
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="information">server_name</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas, l'information de la configuration "server_name" sera utilisé comme valeur du paramètre.
|
|
||||||
Si l'information n'existe pas, la paramètre aura la valeur "None".
|
|
@ -1,26 +0,0 @@
|
|||||||
# Paramètre positionnel
|
|
||||||
|
|
||||||
Déclarons un paramètre positionnel :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param>no</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Créons la fonction correspondante :
|
|
||||||
|
|
||||||
```
|
|
||||||
def return_value(value):
|
|
||||||
return value
|
|
||||||
```
|
|
||||||
|
|
||||||
La variable aura donc "no" comme valeur puisque le paramètre aura la valeur fixe "no".
|
|
||||||
|
|
||||||
# Paramètre nommée
|
|
||||||
|
|
||||||
Déclarons maintenant un paramètre nommée :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param name="valeur">no</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas la fonction return_value sera exécuté avec le paramètre nommé "valeur" dont sa valeur sera "no".
|
|
@ -1,37 +0,0 @@
|
|||||||
# Paramètre de type "texte"
|
|
||||||
|
|
||||||
Dans l'exemple précédent :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="string">no</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Le paramètre est de type texte (ou "string").
|
|
||||||
C'est le type par défaut pour un paramètre.
|
|
||||||
|
|
||||||
# Paramètre de type "nombre"
|
|
||||||
|
|
||||||
Déclarons un paramètre avec un nombre :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="number">1</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Créons la fonction correspondante :
|
|
||||||
|
|
||||||
```
|
|
||||||
def return_value_with_number(value):
|
|
||||||
if value == 1:
|
|
||||||
return 'no'
|
|
||||||
return 'yes'
|
|
||||||
```
|
|
||||||
|
|
||||||
La variable aura donc "no" comme valeur puisque le paramètre aura la valeur fixe "1".
|
|
||||||
|
|
||||||
# Paramètre de type "nil"
|
|
||||||
|
|
||||||
Le paramètre peut être une valeur null (None en python) :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="nil"/>
|
|
||||||
```
|
|
@ -1,55 +0,0 @@
|
|||||||
# Paramètre de type "variable"
|
|
||||||
|
|
||||||
Imaginons que la variable "my_variable" pré-existe. La valeur de la variable sera utilisé comme paramètre :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="variable">my_variable</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
[Les variables meneuses ou suiveuses](../variable/leadership.md) peuvent être utilisé sans soucis commme paramètre.
|
|
||||||
|
|
||||||
## Paramètre avec variable potentiellement non existante
|
|
||||||
|
|
||||||
Suivant le contexte une variable peut exister ou ne pas exister.
|
|
||||||
|
|
||||||
Un paramètre de type "variable" peut être "optional" :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="variable" optional="True">unknow_variable</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Si la variable "unknow_variable" n'existe pas, le paramètre ne sera pas passé à la fonction.
|
|
||||||
|
|
||||||
Si maintenant on créé un nouveau dictionnaire en créant cette variable, la fonction sera exécuté avec le paramètre.
|
|
||||||
|
|
||||||
## Paramètre avec variable potentiellement désactivée
|
|
||||||
|
|
||||||
Si une variable est désactivé, l'utilisation de cette variable peut poser problème.
|
|
||||||
|
|
||||||
Il est possible de ne pas générer d'erreur si une variable est désactivé en utilisant le paramètre "propertyerror" :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="variable" propertyerror="False">variable1</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas, si la variable est désactivé, le paramètre n'est jamais donnée à la fonction de destination.
|
|
||||||
|
|
||||||
## Paramètre avec variable dynamique
|
|
||||||
|
|
||||||
Il est possible de faire un calcul avec comme paramètre [une variable d'une famille dynamique](../family/auto.md) mais pour une suffix particulier.
|
|
||||||
|
|
||||||
Par exemple :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="variable">vardynval1</param>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas, la valeur du paramètre de la fonction sera la valeur de la variable "vardyn" pour la famille ayant le suffix "val1".
|
|
||||||
|
|
||||||
Il peut être utile de récupérer la valeur du suffix dans la fonction, pour cela il suffit de mettre un paramètre de type suffix :
|
|
||||||
|
|
||||||
```
|
|
||||||
<param type="suffix"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans l'exemple précédent la valeur de ce paramètre sera "val1".
|
|
@ -1,9 +0,0 @@
|
|||||||
# Les services
|
|
||||||
|
|
||||||
|
|
||||||
<!ELEMENT services (service*)>
|
|
||||||
|
|
||||||
<!ELEMENT service ((port* | ip* | file* | override*)*) >
|
|
||||||
<!ATTLIST service name CDATA #REQUIRED>
|
|
||||||
<!ATTLIST service manage (True|False) "True">
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
# Fichier
|
|
||||||
|
|
||||||
FIXME
|
|
||||||
|
|
||||||
|
|
||||||
<!ELEMENT file EMPTY>
|
|
||||||
<!ATTLIST file name CDATA #REQUIRED >
|
|
||||||
<!ATTLIST file file_type (UnicodeOption|variable) "UnicodeOption">
|
|
||||||
<!ATTLIST file variable CDATA #IMPLIED>
|
|
||||||
<!ATTLIST file variable_type (variable) "variable">
|
|
||||||
<!ATTLIST file source CDATA #IMPLIED>
|
|
||||||
<!ATTLIST file mode CDATA "0644">
|
|
||||||
<!ATTLIST file owner CDATA "root">
|
|
||||||
<!ATTLIST file group CDATA "root">
|
|
||||||
<!ATTLIST file filelist CDATA #IMPLIED >
|
|
||||||
<!ATTLIST file redefine (True|False) "False">
|
|
||||||
<!ATTLIST file templating (True|False) "True">
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
# IP
|
|
||||||
|
|
||||||
FIXME
|
|
||||||
|
|
||||||
<!ELEMENT ip (#PCDATA)>
|
|
||||||
<!ATTLIST ip iplist CDATA #IMPLIED >
|
|
||||||
<!ATTLIST ip ip_type (NetworkOption|variable) "NetworkOption">
|
|
||||||
<!ATTLIST ip interface_type (UnicodeOption|variable) "UnicodeOption">
|
|
||||||
<!ATTLIST ip interface CDATA #REQUIRED>
|
|
||||||
<!ATTLIST ip netmask_type (NetmaskOption|variable) "NetmaskOption">
|
|
||||||
<!ATTLIST ip netmask CDATA "255.255.255.255">
|
|
@ -1,7 +0,0 @@
|
|||||||
# Override
|
|
||||||
|
|
||||||
FIXME
|
|
||||||
|
|
||||||
<!ELEMENT override EMPTY>
|
|
||||||
<!ATTLIST override source CDATA #IMPLIED >
|
|
||||||
<!ATTLIST override templating (True|False) "True">
|
|
@ -1,7 +0,0 @@
|
|||||||
# Port
|
|
||||||
|
|
||||||
<!ELEMENT port (#PCDATA)>
|
|
||||||
<!ATTLIST port port_type (PortOption|variable) "PortOption">
|
|
||||||
<!ATTLIST port portlist CDATA #IMPLIED >
|
|
||||||
<!ATTLIST port protocol (tcp|udp) "tcp">
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
# Cible de la fonction de type "variable"
|
|
||||||
|
|
||||||
Par défaut une cible est de type variable.
|
|
||||||
|
|
||||||
```
|
|
||||||
<target>my_variable</target>
|
|
||||||
```
|
|
||||||
|
|
||||||
Mais une target peut être optionnelle. C'est à dire que si la variable n'existe pas, l'action ne sera pas associé à cette variable.
|
|
||||||
|
|
||||||
```
|
|
||||||
<target optional='True'>my_variable</target>
|
|
||||||
```
|
|
@ -1,6 +0,0 @@
|
|||||||
# Variable
|
|
||||||
|
|
||||||
- [Une variable](simple.md)
|
|
||||||
- [Variable meneuse ou suiveuse](leadership.md)
|
|
||||||
|
|
||||||
FIXME <!ATTLIST variable test CDATA #IMPLIED>
|
|
@ -1,74 +0,0 @@
|
|||||||
# Variable meneuse ou suiveuse
|
|
||||||
|
|
||||||
## Variable meneuse
|
|
||||||
|
|
||||||
Une variable meneuse est une [variable](simple.md) qui est obligatoirement de type multiple.
|
|
||||||
|
|
||||||
Une variable meneuse peut être obligatoire.
|
|
||||||
|
|
||||||
## Variable suiveuse
|
|
||||||
|
|
||||||
Une variable suiveuse est une variable donc la longueur n'est pas déterminé par elle-même, mais est identique à celle de la variable meneuse dont elle dépend.
|
|
||||||
|
|
||||||
Une variable suiveuse est une variable placer juste derrière une variable meneuse ou une autre variable suiveuse.
|
|
||||||
|
|
||||||
L'ordre de définition des variables suiveuses est important.
|
|
||||||
|
|
||||||
Cette variable peut être de type multiple. Dans ce cas, pour un index determiné, il est possible de mettre plusieurs valeurs à une même variable.
|
|
||||||
|
|
||||||
Une variable suiveuse peut être obligatoire. Cela signifie que lorsqu'une variable meneuse est renseigné, il faut obligatoirement que la variable suiveuse est également une valeur à l'index considéré.
|
|
||||||
Si aucune valeur n'est définit pour la variable meneuse, aucune valeur n'est a spécifié pour la variable suiveuse.
|
|
||||||
|
|
||||||
Le [mode](../mode.md) par défaut d'une variable suiveuse correspond au [mode](../mode.md) de la variable meneuse.
|
|
||||||
|
|
||||||
Si une variable meneuse est caché ou désactivé, les variables suiveuses le seront également.
|
|
||||||
|
|
||||||
## Définition des variables meneuse et suiveuse
|
|
||||||
|
|
||||||
Voici un exemple de définition d'une variable meneuse et de deux variables meneuses :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="leader" multi='True'/>
|
|
||||||
<variable name="follower1"/>
|
|
||||||
<variable name="follower2" multi='True'/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<group leader="leader">
|
|
||||||
<follower>follower1</follower>
|
|
||||||
<follower>follower2</follower>
|
|
||||||
</group>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
Le nom et la description du groupe prendra par défaut le nom et la description de la variable meneuse.
|
|
||||||
|
|
||||||
Il est possible d'en définit d'autres :
|
|
||||||
|
|
||||||
```
|
|
||||||
<constraints>
|
|
||||||
<group leader="leader" name="leadership" description="My leadership">
|
|
||||||
<follower>follower1</follower>
|
|
||||||
<follower>follower2</follower>
|
|
||||||
</group>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ajout d'une nouvelle variable suiveuse
|
|
||||||
|
|
||||||
Pour ajouter, dans un nouveau dictionnaire, une variable suiveuse à notre groupe, rien de plus simple, il suffit de redéfinir un groupe avec cette (ou ces) nouvelle variable :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variables>
|
|
||||||
<family name="family">
|
|
||||||
<variable name="follower3"/>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<group leader="leader">
|
|
||||||
<follower>follower3</follower>
|
|
||||||
</group>
|
|
||||||
</constraints>
|
|
||||||
```
|
|
@ -1,210 +0,0 @@
|
|||||||
# Variable
|
|
||||||
|
|
||||||
## Un variable
|
|
||||||
|
|
||||||
Une variable est forcement dans une [famille](../family/README.md). Il faut donc déjà avoir créer une [famille](../family/README.md).
|
|
||||||
|
|
||||||
Une variable est déjà un nom. C'est à dire qu'on pourra utiliser plus tard la variable via ce nom.
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Description et aide sur la variable
|
|
||||||
|
|
||||||
En plus d'un nom, il est possible de mettre une "description" à la variable. C'est une information "utilisateur" qui nous permettra d'avoir des informations complémentaires sur le contenu de cette variable :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" description="This is a greate variable"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
En plus de la description, il est possible de préciser une aide complémentaire :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" help="This is a greate variable"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Le type de la variable
|
|
||||||
|
|
||||||
Une variable a un type. Ce type permet de définir les valeurs acceptées par cette variable :
|
|
||||||
|
|
||||||
- string : chaine de caractère (type par défaut)
|
|
||||||
- number : un nombre
|
|
||||||
- float : un chiffre flottant
|
|
||||||
- boolean : True ou False si aucune valeur n'est défini la valeur par défaut cette variable sera True
|
|
||||||
- password : un mot de passe
|
|
||||||
- mail : une adresse mail
|
|
||||||
- filename : nom de fichier au sens Unix (exemple : '/etc/passwd')
|
|
||||||
- date : une date au format "%Y-%m-%d" (exemple : "2021-01-30")
|
|
||||||
- unix_user : nom d'utilisateur au sens Unix
|
|
||||||
- ip : n'importe quelle adresse IPv4
|
|
||||||
- cidr : n'importe quelle adresse IPv4 au format CIDR
|
|
||||||
- local_ip : adresse IPv4 sur un réseau local, si l'adresse IPv4 n'est pas local, un warning sera afficher mais la valeur sera accepté tout de même
|
|
||||||
- netmask : masque d'une adresse IPv4
|
|
||||||
- network : adresse réseau
|
|
||||||
- network_cidr : adresse réseau au format CIDR
|
|
||||||
- broadcast : adresse de diffusion
|
|
||||||
- netbios : nom netbios
|
|
||||||
- domain : nom de domaine
|
|
||||||
- hostname : nom d'hôte
|
|
||||||
- web_address : adresse web (http://www.cadoles.com/)
|
|
||||||
- port : port
|
|
||||||
- mac : adresse MAC
|
|
||||||
- schedule : périodicité du schedule, les valeurs possibles sont "none", "daily", "weekly" ou "monthly"
|
|
||||||
- schedulemod : type de schedule, les valeurs possibles sont "pre" ou "post"
|
|
||||||
|
|
||||||
Pour définir le type d'une variable :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" type="number"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable à valeur multiple
|
|
||||||
|
|
||||||
Par défaut une variable ne peut acceuillir qu'une seule valeur. Il peut être utile de pouvoir spécifier plusieurs valeurs à une même variable.
|
|
||||||
|
|
||||||
Pour définir une variable à valeur multiple :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" multi="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mode de la variable
|
|
||||||
|
|
||||||
Le [mode](../mode.md) par défaut d'une variable correspond au [mode](../mode.md) de la [famille](../family/README.md).
|
|
||||||
|
|
||||||
Pour définir le [mode](../mode.md) :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" mode="expert"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable invisible
|
|
||||||
|
|
||||||
Il est possible de cacher une variable.
|
|
||||||
|
|
||||||
Cacher une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service.
|
|
||||||
Par contre cette variable sont accessibles lorsqu'on va l'utiliser.
|
|
||||||
|
|
||||||
Pour cacher une variable :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" hidden="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable désactive
|
|
||||||
|
|
||||||
Il est possible de désactiver une variable.
|
|
||||||
|
|
||||||
Désactiver une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service mais également lorsqu'on va l'utiliser.
|
|
||||||
|
|
||||||
Pour désactiver une variable :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" disabled="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable obligatoire
|
|
||||||
|
|
||||||
Variable dont une valeur est requise :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" mandatory="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Valeur par défaut d'une variable
|
|
||||||
|
|
||||||
Il est possible de fixer les valeurs par défaut d'une variable :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable">
|
|
||||||
<value>value</value>
|
|
||||||
</variable>
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour une variable multiple, il est possible de préciser plusieurs valeurs :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" multi="True">
|
|
||||||
<value>value 1</value>
|
|
||||||
<value>value 2</value>
|
|
||||||
</variable>
|
|
||||||
```
|
|
||||||
|
|
||||||
Si la variable n'est pas pas une [variable meneuse](leadership.md), la première valeur défini dans cette liste sera également la valeur par défaut proposé si on ajoute une nouvelle valeur à cette variable.
|
|
||||||
|
|
||||||
Une valeur par défaut peut également être [une valeur calculer](../fill/README.md).
|
|
||||||
|
|
||||||
## Redéfinir une variable
|
|
||||||
|
|
||||||
Il est possible de définir une variable dans un dictionnaire et de changer son comportement dans une second dictionnaire.
|
|
||||||
|
|
||||||
Attention trois attributs ne sont redéfinisable :
|
|
||||||
|
|
||||||
- name
|
|
||||||
- type
|
|
||||||
- multi
|
|
||||||
|
|
||||||
Créons notre variable :
|
|
||||||
|
|
||||||
<variable name="my_variable"/>
|
|
||||||
|
|
||||||
Et redéfinisons là :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" redefine="True" description="New description"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Créer une variable inexistante
|
|
||||||
|
|
||||||
Il est parfois utile de créer une variable si elle n'existe pas dans un autre dictionnaire :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" exists="False"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Si cette variable existe dans un autre dictionnaire, elle ne sera pas modifié ni recréé
|
|
||||||
|
|
||||||
## Redéfinir une variable si elle existe
|
|
||||||
|
|
||||||
Parfois on veut pouvoir redéfinir une variable mais seulement dans le cas où elle existe déjà :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" redefine="True" exists="True" hidden="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variable à valeur automatiquement modifiée
|
|
||||||
|
|
||||||
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considéré comme modifié quand le serveur sera déployé.
|
|
||||||
|
|
||||||
Voici une variable a valeur automatiquement modifiée :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" auto_save="True">
|
|
||||||
<value>my_value</value>
|
|
||||||
</variable>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas la valeur est fixée à la valeur actuelle.
|
|
||||||
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
|
|
||||||
|
|
||||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont automatiquement modifiées que si elles ont une valeurs.
|
|
||||||
|
|
||||||
Une [variable meneuse ou suiveuse](leadership.md) ne peut pas avoir la propriété auto_save.
|
|
||||||
|
|
||||||
## Variable à valeur en lecture seule automatique
|
|
||||||
|
|
||||||
Une variable avec valeur en lecture seule automatique est une variable dont la valeur ne sera plus modifiable par l'utilisateur quand le serveur sera déployé.
|
|
||||||
|
|
||||||
Voici un variable à valeur en lecture seule automatique :
|
|
||||||
|
|
||||||
```
|
|
||||||
<variable name="my_variable" auto_freeze="True"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans ce cas la valeur est fixée à la valeur actuelle et elle ne sera plus modifiable par l'utilisateur.
|
|
||||||
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
|
|
||||||
|
|
||||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont en lecteur seul que si elles sont une valeurs.
|
|
||||||
|
|
||||||
Une [variable meneuse ou suiveuse](leadership.md) ne peut pas avoir la propriété auto_freeze.
|
|
@ -1,10 +0,0 @@
|
|||||||
# Le conteneur des variables
|
|
||||||
|
|
||||||
La balise "variables" est le conteneur de l'ensemble des [familles](family/README.md) dans laquelle on pourra placer des [variables](variable/README.md) :
|
|
||||||
|
|
||||||
```
|
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables/>
|
|
||||||
</rougail>
|
|
||||||
```
|
|
@ -1,30 +1,5 @@
|
|||||||
"""Rougail method
|
from .loader import load
|
||||||
|
from .objspace import CreoleObjSpace
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from .rougail import Rougail
|
|
||||||
from .annotator import modes
|
from .annotator import modes
|
||||||
|
|
||||||
__ALL__ = ('RougailObjSpace', 'modes')
|
__ALL__ = ('load', 'CreoleObjSpace', 'modes')
|
||||||
|
1405
src/rougail/annotator.py
Normal file
1405
src/rougail/annotator.py
Normal file
@ -0,0 +1,1405 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
from os.path import join, basename
|
||||||
|
from ast import literal_eval
|
||||||
|
import imp
|
||||||
|
|
||||||
|
|
||||||
|
from .i18n import _
|
||||||
|
from .utils import normalize_family
|
||||||
|
from .error import CreoleDictConsistencyError
|
||||||
|
from .xmlreflector import HIGH_COMPATIBILITY
|
||||||
|
|
||||||
|
#mode order is important
|
||||||
|
modes_level = ('basic', 'normal', 'expert')
|
||||||
|
|
||||||
|
|
||||||
|
class Mode(object):
|
||||||
|
def __init__(self, name, level):
|
||||||
|
self.name = name
|
||||||
|
self.level = level
|
||||||
|
|
||||||
|
def __cmp__(self, other):
|
||||||
|
return cmp(self.level, other.level)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.level == other.level
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self.level != other.level
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return other.level < self.level
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return not self.level < other.level
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return not other.level < self.level
|
||||||
|
|
||||||
|
|
||||||
|
def mode_factory():
|
||||||
|
mode_obj = {}
|
||||||
|
for idx in range(len(modes_level)):
|
||||||
|
name = modes_level[idx]
|
||||||
|
mode_obj[name] = Mode(name, idx)
|
||||||
|
return mode_obj
|
||||||
|
|
||||||
|
modes = mode_factory()
|
||||||
|
|
||||||
|
# a CreoleObjSpace's attribute has some annotations
|
||||||
|
# that shall not be present in the exported (flatened) XML
|
||||||
|
ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace',
|
||||||
|
'remove_condition', 'path', 'instance_mode', 'index', 'is_in_leadership',
|
||||||
|
'level', 'submulti') # , '_real_container')
|
||||||
|
ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group')
|
||||||
|
|
||||||
|
FORCE_CHOICE = {'oui/non': ['oui', 'non'],
|
||||||
|
'on/off': ['on', 'off'],
|
||||||
|
'yes/no': ['yes', 'no'],
|
||||||
|
'schedule': ['none', 'daily', 'weekly', 'monthly'],
|
||||||
|
'schedulemod': ['pre', 'post']}
|
||||||
|
|
||||||
|
KEY_TYPE = {'variable': 'symlink',
|
||||||
|
'SymLinkOption': 'symlink',
|
||||||
|
'PortOption': 'port',
|
||||||
|
'UnicodeOption': 'string',
|
||||||
|
'NetworkOption': 'network',
|
||||||
|
'NetmaskOption': 'netmask',
|
||||||
|
'URLOption': 'web_address',
|
||||||
|
'FilenameOption': 'filename'}
|
||||||
|
|
||||||
|
TYPE_PARAM_CHECK = ('string', 'python', 'eole', 'variable')
|
||||||
|
TYPE_PARAM_CONDITION = ('string', 'python', 'number', 'eole', 'variable')
|
||||||
|
TYPE_PARAM_FILL = ('string', 'eole', 'number', 'context', 'variable')
|
||||||
|
|
||||||
|
ERASED_FAMILY_ACTION_ATTRIBUTES = ('index', 'action')
|
||||||
|
|
||||||
|
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAnnotator:
|
||||||
|
"""Manage service's object
|
||||||
|
"""
|
||||||
|
def __init__(self, objectspace):
|
||||||
|
self.space = objectspace.space
|
||||||
|
self.paths = objectspace.paths
|
||||||
|
self.objectspace = objectspace
|
||||||
|
"""for example::
|
||||||
|
<service_access service='ntp'>
|
||||||
|
<port protocol='udp' service_accesslist='ntp_udp'>123</port>
|
||||||
|
<tcpwrapper>ntpd</tcpwrapper>
|
||||||
|
</service_access>
|
||||||
|
"""
|
||||||
|
self.grouplist_conditions = {}
|
||||||
|
self.convert_services()
|
||||||
|
|
||||||
|
def convert_services(self):
|
||||||
|
if hasattr(self.space, 'services'):
|
||||||
|
if hasattr(self.space.services, 'service'):
|
||||||
|
subelts = dict()
|
||||||
|
for idx, service in enumerate(self.space.services.service.values()):
|
||||||
|
family = self.objectspace.family()
|
||||||
|
family.name = 'service{}'.format(idx)
|
||||||
|
family.doc = service.name
|
||||||
|
family.family = OrderedDict()
|
||||||
|
self.convert_service_to_family(family.name, family.family, service)
|
||||||
|
setattr(self.space.services, family.name, family)
|
||||||
|
del self.space.services.service
|
||||||
|
else:
|
||||||
|
del self.space.services
|
||||||
|
|
||||||
|
def convert_service_to_family(self, service_name, service_family, service):
|
||||||
|
for elttype, values in vars(service).items():
|
||||||
|
if elttype in ['name', 'index']:
|
||||||
|
continue
|
||||||
|
family = self.objectspace.family()
|
||||||
|
family.name = elttype + 's'
|
||||||
|
if isinstance(values, dict):
|
||||||
|
values = list(values.values())
|
||||||
|
family.family = self.convert_subelement_service(elttype,
|
||||||
|
values,
|
||||||
|
'services.{}.{}'.format(service_name, family.name))
|
||||||
|
family.mode = None
|
||||||
|
service_family[family.name] = family
|
||||||
|
|
||||||
|
def convert_subelement_service(self, name, elts, path):
|
||||||
|
families = []
|
||||||
|
new_elts = self._reorder_elts(name, elts, True)
|
||||||
|
for index, elt_info in enumerate(new_elts):
|
||||||
|
elt = elt_info['elt']
|
||||||
|
elt_name = elt_info['elt_name']
|
||||||
|
|
||||||
|
# try to launch _update_xxxx() function
|
||||||
|
update_elt = '_update_' + elt_name
|
||||||
|
if hasattr(self, update_elt):
|
||||||
|
getattr(self, update_elt)(elt, index, path)
|
||||||
|
variables = []
|
||||||
|
subpath = '{}.{}{}'.format(path, name, index)
|
||||||
|
listname = '{}list'.format(name)
|
||||||
|
activate_path = '.'.join([subpath, 'activate'])
|
||||||
|
if elt in self.grouplist_conditions:
|
||||||
|
# FIXME transformer le activate qui disparait en boolean
|
||||||
|
self.objectspace.list_conditions.setdefault(listname,
|
||||||
|
{}).setdefault(self.grouplist_conditions[elt],
|
||||||
|
[]).append(activate_path)
|
||||||
|
for key in dir(elt):
|
||||||
|
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
|
||||||
|
continue
|
||||||
|
value = getattr(elt, key)
|
||||||
|
if isinstance(value, list):
|
||||||
|
continue
|
||||||
|
if key == 'service':
|
||||||
|
value = value.name
|
||||||
|
if key == listname:
|
||||||
|
self.objectspace.list_conditions.setdefault(listname,
|
||||||
|
{}).setdefault(
|
||||||
|
value,
|
||||||
|
[]).append(activate_path)
|
||||||
|
continue
|
||||||
|
if key == 'name':
|
||||||
|
true_key = elt_name
|
||||||
|
else:
|
||||||
|
true_key = key
|
||||||
|
if true_key in self.objectspace.booleans_attributs:
|
||||||
|
type_ = 'boolean'
|
||||||
|
else:
|
||||||
|
type_ = 'string'
|
||||||
|
dtd_key_type = true_key + '_type'
|
||||||
|
|
||||||
|
if hasattr(elt, dtd_key_type):
|
||||||
|
type_ = KEY_TYPE[getattr(elt, dtd_key_type)]
|
||||||
|
multi = isinstance(value, list)
|
||||||
|
variables.append(self._generate_element(elt_name,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
type_,
|
||||||
|
subpath,
|
||||||
|
multi))
|
||||||
|
# FIXME ne devrait pas etre True par défaut
|
||||||
|
variables.append(self._generate_element(name, 'activate', True, 'boolean', subpath))
|
||||||
|
family = self.objectspace.family()
|
||||||
|
family.name = '{}{}'.format(name, index)
|
||||||
|
family.variable = variables
|
||||||
|
family.mode = None
|
||||||
|
self.paths.append('family', subpath, 'services', creoleobj=family)
|
||||||
|
families.append(family)
|
||||||
|
return families
|
||||||
|
|
||||||
|
def _generate_element(self, eltname, name, value, type_, subpath, multi=False):
|
||||||
|
var_data = {'name': name, 'doc': '', 'value': value,
|
||||||
|
'auto_freeze': False, 'mode': None, 'multi': multi}
|
||||||
|
values = None
|
||||||
|
if type_ == 'string':
|
||||||
|
values = self.objectspace.forced_choice_option.get(eltname, {}).get(name)
|
||||||
|
if values is not None:
|
||||||
|
type_ = 'choice'
|
||||||
|
var_data['type'] = type_
|
||||||
|
|
||||||
|
variable = self.objectspace.variable()
|
||||||
|
variable.mandatory = True
|
||||||
|
for key, value in var_data.items():
|
||||||
|
if key == 'value':
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
if type_ == 'symlink':
|
||||||
|
key = 'opt'
|
||||||
|
else:
|
||||||
|
# Value is a list of objects
|
||||||
|
if not multi:
|
||||||
|
val = self.objectspace.value()
|
||||||
|
val.name = value
|
||||||
|
value = [val]
|
||||||
|
else:
|
||||||
|
value_list = []
|
||||||
|
for valiter in value:
|
||||||
|
val = self.objectspace.value()
|
||||||
|
val.name = valiter.name
|
||||||
|
value_list.append(val)
|
||||||
|
value = value_list
|
||||||
|
if key == 'doc' and type_ == 'symlink':
|
||||||
|
continue
|
||||||
|
setattr(variable, key, value)
|
||||||
|
if values is not None:
|
||||||
|
choices = []
|
||||||
|
for value in values:
|
||||||
|
choice = self.objectspace.choice()
|
||||||
|
choice.name = value
|
||||||
|
choices.append(choice)
|
||||||
|
variable.choice = choices
|
||||||
|
path = '{}.{}'.format(subpath, name)
|
||||||
|
self.paths.append('variable', path, 'services', 'service', variable)
|
||||||
|
return variable
|
||||||
|
|
||||||
|
def _update_file(self, file_, index, service_path):
|
||||||
|
if file_.file_type == "UnicodeOption":
|
||||||
|
if not hasattr(file_, 'source'):
|
||||||
|
file_.source = basename(file_.name)
|
||||||
|
elif not hasattr(file_, 'source'):
|
||||||
|
raise CreoleDictConsistencyError(_('attribute source mandatory for file with variable name '
|
||||||
|
'for {}').format(file_.name))
|
||||||
|
|
||||||
|
def _reorder_elts(self, name, elts, duplicate_list):
|
||||||
|
"""Reorders by index the elts
|
||||||
|
"""
|
||||||
|
dict_elts = OrderedDict()
|
||||||
|
# reorder elts by index
|
||||||
|
new_elts = {}
|
||||||
|
not_indexed = []
|
||||||
|
for elt in elts:
|
||||||
|
idx = elt.index
|
||||||
|
new_elts.setdefault(idx, []).append(elt)
|
||||||
|
idxes = list(new_elts.keys())
|
||||||
|
idxes.sort()
|
||||||
|
elts = not_indexed
|
||||||
|
for idx in idxes:
|
||||||
|
elts.extend(new_elts[idx])
|
||||||
|
for idx, elt in enumerate(elts):
|
||||||
|
elt_added = False
|
||||||
|
for key in dir(elt):
|
||||||
|
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
|
||||||
|
continue
|
||||||
|
value = getattr(elt, key)
|
||||||
|
if not elt_added:
|
||||||
|
eltname = elt.name
|
||||||
|
dict_elts.setdefault(eltname, []).append({'elt_name': name, 'elt': elt})
|
||||||
|
|
||||||
|
result_elts = []
|
||||||
|
for elt in dict_elts.values():
|
||||||
|
result_elts.extend(elt)
|
||||||
|
return result_elts
|
||||||
|
|
||||||
|
def make_group_from_elts(self, name, elts, path, duplicate_list):
|
||||||
|
"""Splits each objects into a group (and `OptionDescription`, in tiramisu terms)
|
||||||
|
and build elements and its attributes (the `Options` in tiramisu terms)
|
||||||
|
"""
|
||||||
|
families = []
|
||||||
|
new_elts = self._reorder_elts(name, elts, duplicate_list)
|
||||||
|
for index, elt_info in enumerate(new_elts):
|
||||||
|
elt = elt_info['elt']
|
||||||
|
elt_name = elt_info['elt_name']
|
||||||
|
|
||||||
|
# try to launch _update_xxxx() function
|
||||||
|
update_elt = '_update_' + elt_name
|
||||||
|
if hasattr(self, update_elt):
|
||||||
|
getattr(self, update_elt)(elt, index, path)
|
||||||
|
variables = []
|
||||||
|
subpath = '{}.{}{}'.format(path, name, index)
|
||||||
|
listname = '{}list'.format(name)
|
||||||
|
activate_path = '.'.join([subpath, 'activate'])
|
||||||
|
if elt in self.grouplist_conditions:
|
||||||
|
# FIXME transformer le activate qui disparait en boolean
|
||||||
|
self.objectspace.list_conditions.setdefault(listname,
|
||||||
|
{}).setdefault(self.grouplist_conditions[elt],
|
||||||
|
[]).append(activate_path)
|
||||||
|
for key in dir(elt):
|
||||||
|
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
|
||||||
|
continue
|
||||||
|
value = getattr(elt, key)
|
||||||
|
if isinstance(value, list) and duplicate_list:
|
||||||
|
# FIXME plusieurs fichier si calculé !
|
||||||
|
continue
|
||||||
|
if key == listname:
|
||||||
|
self.objectspace.list_conditions.setdefault(listname,
|
||||||
|
{}).setdefault(
|
||||||
|
value,
|
||||||
|
[]).append(activate_path)
|
||||||
|
continue
|
||||||
|
if key in self.objectspace.booleans_attributs:
|
||||||
|
type_ = 'boolean'
|
||||||
|
else:
|
||||||
|
type_ = 'string'
|
||||||
|
dtd_key_type = key + '_type'
|
||||||
|
if hasattr(elt, dtd_key_type):
|
||||||
|
type_ = KEY_TYPE[getattr(elt, dtd_key_type)]
|
||||||
|
multi = isinstance(value, list)
|
||||||
|
variables.append(self._generate_element(elt_name,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
type_,
|
||||||
|
subpath,
|
||||||
|
multi))
|
||||||
|
# FIXME ne devrait pas etre True par défaut
|
||||||
|
variables.append(self._generate_element(name, 'activate', True, 'boolean', subpath))
|
||||||
|
family = self.objectspace.family()
|
||||||
|
family.name = '{}{}'.format(name, index)
|
||||||
|
family.variable = variables
|
||||||
|
family.mode = None
|
||||||
|
self.paths.append('family', subpath, 'services', creoleobj=family)
|
||||||
|
families.append(family)
|
||||||
|
return families
|
||||||
|
|
||||||
|
|
||||||
|
class ActionAnnotator(ServiceAnnotator):
|
||||||
|
def __init__(self, objectspace):
|
||||||
|
self.space = objectspace.space
|
||||||
|
self.paths = objectspace.paths
|
||||||
|
self.objectspace = objectspace
|
||||||
|
self.grouplist_conditions = {}
|
||||||
|
self.convert_family_action()
|
||||||
|
|
||||||
|
def convert_family_action(self):
|
||||||
|
if hasattr(self.space, 'family_action'):
|
||||||
|
actions = self.objectspace.family()
|
||||||
|
actions.name = 'actions'
|
||||||
|
actions.mode = None
|
||||||
|
actions.family = []
|
||||||
|
self.space.actions = actions
|
||||||
|
namespaces = []
|
||||||
|
for name, actions in self.space.family_action.items():
|
||||||
|
subpath = 'actions.{}'.format(normalize_family(name))
|
||||||
|
for action in actions.action:
|
||||||
|
namespace = action.namespace
|
||||||
|
if namespace in namespaces:
|
||||||
|
raise CreoleDictConsistencyError(_('only one action allow for {}'
|
||||||
|
'').format(namespace))
|
||||||
|
namespaces.append(namespace)
|
||||||
|
action.name = action.namespace
|
||||||
|
new_actions = self.make_group_from_elts('action', actions.action, subpath, False)
|
||||||
|
family = self.objectspace.family()
|
||||||
|
family.name = actions.name
|
||||||
|
family.family = new_actions
|
||||||
|
family.mode = None
|
||||||
|
variables = []
|
||||||
|
for key, value in vars(actions).items():
|
||||||
|
if key not in ERASED_FAMILY_ACTION_ATTRIBUTES:
|
||||||
|
variables.append(self._generate_element('action', key, value, 'string',
|
||||||
|
subpath))
|
||||||
|
family.variable = variables
|
||||||
|
self.space.actions.family.append(family)
|
||||||
|
del self.space.family_action
|
||||||
|
|
||||||
|
|
||||||
|
class SpaceAnnotator(object):
|
||||||
|
"""Transformations applied on a CreoleObjSpace instance
|
||||||
|
"""
|
||||||
|
def __init__(self, objectspace, eosfunc_file):
|
||||||
|
self.paths = objectspace.paths
|
||||||
|
self.space = objectspace.space
|
||||||
|
self.objectspace = objectspace
|
||||||
|
self.valid_enums = {}
|
||||||
|
self.force_value = {}
|
||||||
|
self.has_calc = []
|
||||||
|
self.force_no_value = []
|
||||||
|
self.force_not_mandatory = []
|
||||||
|
if eosfunc_file:
|
||||||
|
self.eosfunc = imp.load_source('eosfunc', eosfunc_file)
|
||||||
|
else:
|
||||||
|
self.eosfunc = None
|
||||||
|
if HIGH_COMPATIBILITY:
|
||||||
|
self.default_has_no_value = []
|
||||||
|
self.has_frozen_if_in_condition = []
|
||||||
|
self.default_variable_options()
|
||||||
|
self.variable_submulti()
|
||||||
|
self.convert_auto_freeze()
|
||||||
|
self.convert_groups()
|
||||||
|
self.filter_check()
|
||||||
|
self.filter_condition()
|
||||||
|
self.convert_valid_enums()
|
||||||
|
self.convert_check()
|
||||||
|
self.convert_autofill()
|
||||||
|
self.remove_empty_families()
|
||||||
|
self.change_variable_mode()
|
||||||
|
self.change_family_mode()
|
||||||
|
self.dynamic_families()
|
||||||
|
self.filter_separators()
|
||||||
|
self.absolute_path_for_symlink_in_services()
|
||||||
|
self.convert_helps()
|
||||||
|
if hasattr(self.space, 'constraints'):
|
||||||
|
del self.space.constraints.index
|
||||||
|
if vars(self.space.constraints):
|
||||||
|
raise Exception('constraints again?')
|
||||||
|
del self.space.constraints
|
||||||
|
|
||||||
|
def absolute_path_for_symlink_in_services(self):
|
||||||
|
if not hasattr(self.space, 'services'):
|
||||||
|
return
|
||||||
|
families = vars(self.space.services).values()
|
||||||
|
for family in families:
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
for fam in family.family.values():
|
||||||
|
for fam1 in fam.family:
|
||||||
|
for variable in fam1.variable:
|
||||||
|
if variable.type == 'symlink' and '.' not in variable.name:
|
||||||
|
variable.opt = self.paths.get_variable_path(variable.opt, 'creole')
|
||||||
|
|
||||||
|
def convert_helps(self):
|
||||||
|
# FIXME l'aide doit etre dans la variable!
|
||||||
|
if not hasattr(self.space, 'help'):
|
||||||
|
return
|
||||||
|
helps = self.space.help
|
||||||
|
if hasattr(helps, 'variable'):
|
||||||
|
for hlp in helps.variable.values():
|
||||||
|
variable = self.paths.get_variable_obj(hlp.name)
|
||||||
|
variable.help = hlp.text
|
||||||
|
if hasattr(helps, 'family'):
|
||||||
|
for hlp in helps.family.values():
|
||||||
|
variable = self.paths.get_family_obj(hlp.name)
|
||||||
|
variable.help = hlp.text
|
||||||
|
del self.space.help
|
||||||
|
|
||||||
|
def convert_groups(self): # pylint: disable=C0111
|
||||||
|
if hasattr(self.space, 'constraints'):
|
||||||
|
if hasattr(self.space.constraints, 'group'):
|
||||||
|
for group in self.space.constraints.group:
|
||||||
|
leader_fullname = group.master
|
||||||
|
follower_names = list(group.slave.keys())
|
||||||
|
leader_family_name = self.paths.get_variable_family_name(leader_fullname)
|
||||||
|
namespace = self.paths.get_variable_namespace(leader_fullname)
|
||||||
|
leader_name = self.paths.get_variable_name(leader_fullname)
|
||||||
|
leader_family = self.space.variables[namespace].family[leader_family_name]
|
||||||
|
leader_path = namespace + '.' + leader_family_name
|
||||||
|
is_leader = False
|
||||||
|
for variable in list(leader_family.variable.values()):
|
||||||
|
if isinstance(variable, self.objectspace.Leadership):
|
||||||
|
# append follower to an existed leadership
|
||||||
|
if variable.name == leader_name:
|
||||||
|
leader_space = variable
|
||||||
|
is_leader = True
|
||||||
|
else:
|
||||||
|
if is_leader:
|
||||||
|
if variable.name == follower_names[0]:
|
||||||
|
# followers are multi
|
||||||
|
if not variable.multi:
|
||||||
|
raise CreoleDictConsistencyError(_('the variable {} in a group must be multi or submulti').format(variable.name))
|
||||||
|
follower_names.remove(variable.name)
|
||||||
|
leader_family.variable.pop(variable.name)
|
||||||
|
leader_space.variable.append(variable) # pylint: disable=E1101
|
||||||
|
if namespace == 'creole':
|
||||||
|
variable_fullpath = variable.name
|
||||||
|
else:
|
||||||
|
variable_fullpath = leader_path + '.' + variable.name
|
||||||
|
self.paths.set_leader(variable_fullpath, leader_name)
|
||||||
|
if follower_names == []:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot found this follower {}').format(follower_names[0]))
|
||||||
|
if is_leader is False and variable.name == leader_name:
|
||||||
|
leader_space = self.objectspace.Leadership()
|
||||||
|
leader_space.variable = []
|
||||||
|
leader_space.name = leader_name
|
||||||
|
leader_space.hidden = variable.hidden
|
||||||
|
if hasattr(group, 'description'):
|
||||||
|
leader_space.doc = group.description
|
||||||
|
else:
|
||||||
|
leader_space.doc = variable.description
|
||||||
|
variable.hidden = None
|
||||||
|
self.paths.append('family', leader_path + '.' + leader_name, namespace, creoleobj=leader_space)
|
||||||
|
# manage leader's variable
|
||||||
|
if variable.multi is not True:
|
||||||
|
raise CreoleDictConsistencyError(_('the variable {} in a group must be multi').format(variable.name))
|
||||||
|
leader_family.variable[leader_name] = leader_space
|
||||||
|
leader_space.variable.append(variable) # pylint: disable=E1101
|
||||||
|
self.paths.set_leader(leader_fullname, leader_name)
|
||||||
|
leader_space.path = leader_fullname
|
||||||
|
is_leader = True
|
||||||
|
else:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot found followers {}').format(follower_names))
|
||||||
|
del self.space.constraints.group
|
||||||
|
|
||||||
|
def remove_empty_families(self): # pylint: disable=C0111,R0201
|
||||||
|
if hasattr(self.space, 'variables'):
|
||||||
|
for family in self.space.variables.values():
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
space = family.family
|
||||||
|
removed_families = []
|
||||||
|
for family_name, family in space.items():
|
||||||
|
if not hasattr(family, 'variable') or len(family.variable) == 0:
|
||||||
|
removed_families.append(family_name)
|
||||||
|
del space[family_name]
|
||||||
|
# remove help too
|
||||||
|
if hasattr(self.space, 'help') and hasattr(self.space.help, 'family'):
|
||||||
|
for family in self.space.help.family.keys():
|
||||||
|
if family in removed_families:
|
||||||
|
del self.space.help.family[family]
|
||||||
|
|
||||||
|
def change_family_mode(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.space, 'variables'):
|
||||||
|
return
|
||||||
|
for family in self.space.variables.values():
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
for family in family.family.values():
|
||||||
|
mode = modes_level[-1]
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if isinstance(variable, self.objectspace.Leadership):
|
||||||
|
variable_mode = variable.variable[0].mode
|
||||||
|
variable.variable[0].mode = None
|
||||||
|
variable.mode = variable_mode
|
||||||
|
else:
|
||||||
|
variable_mode = variable.mode
|
||||||
|
if variable_mode is not None and modes[mode] > modes[variable_mode]:
|
||||||
|
mode = variable_mode
|
||||||
|
family.mode = mode
|
||||||
|
|
||||||
|
def dynamic_families(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.space, 'variables'):
|
||||||
|
return
|
||||||
|
for family in self.space.variables.values():
|
||||||
|
if hasattr(family, 'family'):
|
||||||
|
for family in family.family.values():
|
||||||
|
if 'dynamic' in vars(family):
|
||||||
|
namespace = self.paths.get_variable_namespace(family.dynamic)
|
||||||
|
varpath = self.paths.get_variable_path(family.dynamic, namespace)
|
||||||
|
family.dynamic = varpath
|
||||||
|
|
||||||
|
def _annotate_variable(self, variable, family_mode, path, is_follower=False):
|
||||||
|
if (HIGH_COMPATIBILITY and variable.type == 'choice' and variable.mode != modes_level[-1] and variable.mandatory is True and path in self.default_has_no_value):
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
if variable.type == 'choice' and is_follower and family_mode == modes_level[0] and variable.mandatory is True:
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
# if the variable is mandatory and doesn't have any value
|
||||||
|
# then the variable's mode is set to 'basic'
|
||||||
|
has_value = hasattr(variable, 'value')
|
||||||
|
if (path not in self.has_calc and variable.mandatory is True and
|
||||||
|
(not has_value or is_follower) and variable.type != 'choice'):
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
if has_value:
|
||||||
|
if not HIGH_COMPATIBILITY or (not path.startswith('creole.services.') \
|
||||||
|
and path not in self.force_no_value and path not in self.force_not_mandatory):
|
||||||
|
variable.mandatory = True
|
||||||
|
if variable.hidden is True:
|
||||||
|
variable.frozen = True
|
||||||
|
if not variable.auto_save is True and 'force_default_on_freeze' not in vars(variable):
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
if variable.name == 'frozen' and not variable.auto_save is True:
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
if variable.mode != None and not is_follower and modes[variable.mode] < modes[family_mode]:
|
||||||
|
variable.mode = family_mode
|
||||||
|
if variable.mode != None and variable.mode != modes_level[0] and modes[variable.mode] < modes[family_mode]:
|
||||||
|
variable.mode = family_mode
|
||||||
|
|
||||||
|
def default_variable_options(self):
|
||||||
|
if hasattr(self.space, 'variables'):
|
||||||
|
for families in self.space.variables.values():
|
||||||
|
if hasattr(families, 'family'):
|
||||||
|
for family in families.family.values():
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if not hasattr(variable, 'type'):
|
||||||
|
variable.type = 'string'
|
||||||
|
if variable.type != 'symlink' and not hasattr(variable, 'description'):
|
||||||
|
variable.description = variable.name
|
||||||
|
|
||||||
|
def variable_submulti(self):
|
||||||
|
if hasattr(self.space, 'variables'):
|
||||||
|
for families in self.space.variables.values():
|
||||||
|
if hasattr(families, 'family'):
|
||||||
|
for family in families.family.values():
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if variable.submulti:
|
||||||
|
variable.multi = 'submulti'
|
||||||
|
|
||||||
|
def convert_auto_freeze(self): # pylint: disable=C0111
|
||||||
|
if hasattr(self.space, 'variables'):
|
||||||
|
for variables in self.space.variables.values():
|
||||||
|
if hasattr(variables, 'family'):
|
||||||
|
for family in variables.family.values():
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if variable.auto_freeze:
|
||||||
|
new_condition = self.objectspace.condition()
|
||||||
|
new_condition.name = 'auto_frozen_if_in'
|
||||||
|
new_condition.namespace = variables.name
|
||||||
|
new_condition.source = FREEZE_AUTOFREEZE_VARIABLE
|
||||||
|
new_param = self.objectspace.param()
|
||||||
|
new_param.text = 'oui'
|
||||||
|
new_condition.param = [new_param]
|
||||||
|
new_target = self.objectspace.target()
|
||||||
|
new_target.type = 'variable'
|
||||||
|
if variables.name == 'creole':
|
||||||
|
path = variable.name
|
||||||
|
else:
|
||||||
|
path = variable.namespace + '.' + family.name + '.' + variable.name
|
||||||
|
new_target.name = path
|
||||||
|
new_condition.target = [new_target]
|
||||||
|
if not hasattr(self.space.constraints, 'condition'):
|
||||||
|
self.space.constraints.condition = []
|
||||||
|
self.space.constraints.condition.append(new_condition)
|
||||||
|
|
||||||
|
def _set_valid_enum(self, variable, values, type_):
|
||||||
|
if isinstance(values, list):
|
||||||
|
variable.mandatory = True
|
||||||
|
variable.choice = []
|
||||||
|
choices = []
|
||||||
|
for value in values:
|
||||||
|
choice = self.objectspace.choice()
|
||||||
|
choice.name = str(value)
|
||||||
|
choices.append(choice.name)
|
||||||
|
choice.type = type_
|
||||||
|
variable.choice.append(choice)
|
||||||
|
if not variable.choice:
|
||||||
|
raise CreoleDictConsistencyError(_('empty valid enum is not allowed for variable {}').format(variable.name))
|
||||||
|
if hasattr(variable, 'value'):
|
||||||
|
for value in variable.value:
|
||||||
|
value.type = type_
|
||||||
|
if value.name not in choices:
|
||||||
|
raise CreoleDictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, variable.name, choices))
|
||||||
|
else:
|
||||||
|
new_value = self.objectspace.value()
|
||||||
|
new_value.name = values[0]
|
||||||
|
new_value.type = type_
|
||||||
|
variable.value = [new_value]
|
||||||
|
else:
|
||||||
|
# probe choice
|
||||||
|
variable.choice = values
|
||||||
|
variable.type = 'choice'
|
||||||
|
|
||||||
|
def _convert_valid_enum(self, variable, path):
|
||||||
|
if variable.type in FORCE_CHOICE:
|
||||||
|
if path in self.valid_enums:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot set valid enum for variable with type {}').format(variable.type))
|
||||||
|
self._set_valid_enum(variable, FORCE_CHOICE[variable.type], 'string')
|
||||||
|
if path in self.valid_enums:
|
||||||
|
values = self.valid_enums[path]['values']
|
||||||
|
self._set_valid_enum(variable, values, variable.type)
|
||||||
|
del self.valid_enums[path]
|
||||||
|
if path in self.force_value:
|
||||||
|
new_value = self.objectspace.value()
|
||||||
|
new_value.name = self.force_value[path]
|
||||||
|
variable.value = [new_value]
|
||||||
|
del self.force_value[path]
|
||||||
|
|
||||||
|
def convert_valid_enums(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.space, 'variables'):
|
||||||
|
return
|
||||||
|
for variables in self.space.variables.values():
|
||||||
|
namespace = variables.name
|
||||||
|
if hasattr(variables, 'family'):
|
||||||
|
for family in variables.family.values():
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
if isinstance(variable, self.objectspace.Leadership):
|
||||||
|
for follower in variable.variable:
|
||||||
|
path = '{}.{}.{}.{}'.format(namespace, family.name, variable.name, follower.name)
|
||||||
|
self._convert_valid_enum(follower, path)
|
||||||
|
else:
|
||||||
|
path = '{}.{}.{}'.format(namespace, family.name, variable.name)
|
||||||
|
self._convert_valid_enum(variable, path)
|
||||||
|
# valid_enums must be empty now (all information are store in objects)
|
||||||
|
if self.valid_enums:
|
||||||
|
raise CreoleDictConsistencyError(_('valid_enum sets for unknown variables {}').format(self.valid_enums.keys()))
|
||||||
|
|
||||||
|
def change_variable_mode(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.space, 'variables'):
|
||||||
|
return
|
||||||
|
for variables in self.space.variables.values():
|
||||||
|
if hasattr(variables, 'family'):
|
||||||
|
for family in variables.family.values():
|
||||||
|
family_mode = family.mode
|
||||||
|
if hasattr(family, 'variable'):
|
||||||
|
for variable in family.variable.values():
|
||||||
|
|
||||||
|
if isinstance(variable, self.objectspace.Leadership):
|
||||||
|
mode = modes_level[-1]
|
||||||
|
for follower in variable.variable:
|
||||||
|
if follower.auto_save is True:
|
||||||
|
raise CreoleDictConsistencyError(_('leader/followers {} '
|
||||||
|
'could not be '
|
||||||
|
'auto_save').format(follower.name))
|
||||||
|
if follower.auto_freeze is True:
|
||||||
|
raise CreoleDictConsistencyError(_('leader/followers {} '
|
||||||
|
'could not be '
|
||||||
|
'auto_freeze').format(follower.name))
|
||||||
|
if HIGH_COMPATIBILITY and variable.name != follower.name: # and variable.variable[0].mode != modes_level[0]:
|
||||||
|
is_follower = True
|
||||||
|
else:
|
||||||
|
is_follower = False
|
||||||
|
path = '{}.{}.{}'.format(family.path, variable.name, follower.name)
|
||||||
|
self._annotate_variable(follower, family_mode, path, is_follower)
|
||||||
|
# leader's mode is minimum level
|
||||||
|
if modes[variable.variable[0].mode] > modes[follower.mode]:
|
||||||
|
follower.mode = variable.variable[0].mode
|
||||||
|
variable.mode = variable.variable[0].mode
|
||||||
|
else:
|
||||||
|
# auto_save's variable is set in 'basic' mode if its mode is 'normal'
|
||||||
|
if variable.auto_save is True and variable.mode != modes_level[-1]:
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
# auto_freeze's variable is set in 'basic' mode if its mode is 'normal'
|
||||||
|
if variable.auto_freeze is True and variable.mode != modes_level[-1]:
|
||||||
|
variable.mode = modes_level[0]
|
||||||
|
path = '{}.{}'.format(family.path, variable.name)
|
||||||
|
self._annotate_variable(variable, family_mode, path)
|
||||||
|
|
||||||
|
def get_variable(self, name): # pylint: disable=C0111
|
||||||
|
return self.paths.get_variable_obj(name)
|
||||||
|
|
||||||
|
def convert_autofill(self): # pylint: disable=C0111
|
||||||
|
if hasattr(self.space, 'constraints'):
|
||||||
|
self.convert_duplicate_autofill(self.space.constraints)
|
||||||
|
if 'auto' in vars(self.space.constraints):
|
||||||
|
self.convert_auto(self.space.constraints.auto, self.space)
|
||||||
|
if 'fill' in vars(self.space.constraints):
|
||||||
|
self.convert_fill(self.space.constraints.fill, self.space)
|
||||||
|
|
||||||
|
def convert_duplicate_autofill(self, constraints):
|
||||||
|
""" Remove duplicate auto or fill for a variable
|
||||||
|
This variable must be redefined
|
||||||
|
"""
|
||||||
|
fills = {}
|
||||||
|
# sort fill/auto by index
|
||||||
|
if 'fill' in vars(constraints):
|
||||||
|
for idx, fill in enumerate(constraints.fill):
|
||||||
|
fills[fill.index] = {'idx': idx, 'fill': fill, 'type': 'fill'}
|
||||||
|
if 'auto' in vars(constraints):
|
||||||
|
for idx, fill in enumerate(constraints.auto):
|
||||||
|
fills[fill.index] = {'idx': idx, 'fill': fill, 'type': 'auto'}
|
||||||
|
indexes = list(fills.keys())
|
||||||
|
indexes.sort()
|
||||||
|
targets = {}
|
||||||
|
remove_autos = []
|
||||||
|
remove_fills = []
|
||||||
|
for idx in indexes:
|
||||||
|
fill = fills[idx]['fill']
|
||||||
|
redefine = bool(fill.redefine)
|
||||||
|
if fill.target in targets:
|
||||||
|
if redefine:
|
||||||
|
if targets[fill.target][1] == 'auto':
|
||||||
|
remove_autos.append(targets[fill.target][0])
|
||||||
|
else:
|
||||||
|
remove_fills.append(targets[fill.target][0])
|
||||||
|
else:
|
||||||
|
raise CreoleDictConsistencyError(_("An auto or fill already exists "
|
||||||
|
"for the target: {}").format(
|
||||||
|
fill.target))
|
||||||
|
targets[fill.target] = (fills[idx]['idx'], fills[idx]['type'])
|
||||||
|
remove_autos.sort(reverse=True)
|
||||||
|
for idx in remove_autos:
|
||||||
|
constraints.auto.pop(idx)
|
||||||
|
remove_fills.sort(reverse=True)
|
||||||
|
for idx in remove_fills:
|
||||||
|
constraints.fill.pop(idx)
|
||||||
|
|
||||||
|
def convert_auto(self, auto_space, space): # pylint: disable=C0111
|
||||||
|
for auto in auto_space:
|
||||||
|
if HIGH_COMPATIBILITY and auto.target in self.has_frozen_if_in_condition:
|
||||||
|
# if a variable has a 'frozen_if_in' condition
|
||||||
|
# then we change the 'auto' variable as a 'fill' variable
|
||||||
|
continue
|
||||||
|
# an auto is a fill with "hidden" and "frozen" properties
|
||||||
|
variable = self.get_variable(auto.target)
|
||||||
|
if variable.auto_freeze:
|
||||||
|
raise CreoleDictConsistencyError(_('variable with auto value '
|
||||||
|
'cannot be auto_freeze').format(auto.target))
|
||||||
|
if variable.auto_save:
|
||||||
|
raise CreoleDictConsistencyError(_('variable with auto value '
|
||||||
|
'cannot be auto_save').format(auto.target))
|
||||||
|
leader = self.paths.get_leader(auto.target)
|
||||||
|
if leader is None or variable.name != leader:
|
||||||
|
variable.hidden = True
|
||||||
|
else:
|
||||||
|
leadership = self.paths.get_family_obj(self.paths.get_variable_family_path(auto.target))
|
||||||
|
leadership.hidden = True
|
||||||
|
variable.frozen = True
|
||||||
|
variable.force_default_on_freeze = True
|
||||||
|
if 'fill' not in vars(space.constraints):
|
||||||
|
space.constraints.fill = []
|
||||||
|
space.constraints.fill.extend(auto_space)
|
||||||
|
del space.constraints.auto
|
||||||
|
|
||||||
|
def filter_separators(self): # pylint: disable=C0111,R0201
|
||||||
|
# FIXME devrait etre dans la variable
|
||||||
|
if not hasattr(self.space, 'variables'):
|
||||||
|
return
|
||||||
|
for family in self.space.variables.values():
|
||||||
|
if (hasattr(family, 'separators') and hasattr(family.separators, 'separator')):
|
||||||
|
space = family.separators.separator
|
||||||
|
names = []
|
||||||
|
for idx, separator in enumerate(space):
|
||||||
|
namespace = self.paths.get_variable_namespace(separator.name)
|
||||||
|
subpath = self.paths.get_variable_path(separator.name, namespace)
|
||||||
|
separator.name = subpath
|
||||||
|
if separator.name in names:
|
||||||
|
raise CreoleDictConsistencyError(_('{} already has a separator').format(separator.name))
|
||||||
|
names.append(separator.name)
|
||||||
|
|
||||||
|
|
||||||
|
def load_params_in_validenum(self, param):
|
||||||
|
if param.type in ['string', 'python', 'number']:
|
||||||
|
if not hasattr(param, 'text') and (param.type == 'python' or param.type == 'number'):
|
||||||
|
raise CreoleDictConsistencyError(_("All '{}' variables shall be set in order to calculate {}").format(param.type, 'valid_enum'))
|
||||||
|
if param.type in ['string', 'number']:
|
||||||
|
try:
|
||||||
|
values = literal_eval(param.text)
|
||||||
|
except ValueError:
|
||||||
|
raise CreoleDictConsistencyError(_('Cannot load {}').format(param.text))
|
||||||
|
elif param.type == 'python':
|
||||||
|
try:
|
||||||
|
values = eval(param.text, {'eosfunc': self.eosfunc, '__builtins__': {'range': range, 'str': str}})
|
||||||
|
#FIXME : eval('[str(i) for i in range(3, 13)]', {'eosfunc': eosfunc, '__builtins__': {'range': range, 'str': str}})
|
||||||
|
except NameError:
|
||||||
|
raise CreoleDictConsistencyError(_('The function {} is unknown').format(param.text))
|
||||||
|
if not isinstance(values, list):
|
||||||
|
raise CreoleDictConsistencyError(_('Function {} shall return a list').format(param.text))
|
||||||
|
new_values = []
|
||||||
|
for val in values:
|
||||||
|
new_values.append(val)
|
||||||
|
values = new_values
|
||||||
|
else:
|
||||||
|
values = param.text
|
||||||
|
return values
|
||||||
|
|
||||||
|
def filter_check(self): # pylint: disable=C0111
|
||||||
|
# valid param in check
|
||||||
|
if not hasattr(self.space, 'constraints') or not hasattr(self.space.constraints, 'check'):
|
||||||
|
return
|
||||||
|
space = self.space.constraints.check
|
||||||
|
remove_indexes = []
|
||||||
|
for check_idx, check in enumerate(space):
|
||||||
|
namespace = check.namespace
|
||||||
|
if hasattr(check, 'param'):
|
||||||
|
param_option_indexes = []
|
||||||
|
for idx, param in enumerate(check.param):
|
||||||
|
if param.type not in TYPE_PARAM_CHECK:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot use {} type as a param in check for {}').format(param.type, check.target))
|
||||||
|
if param.type == 'eole':
|
||||||
|
param.type = 'variable'
|
||||||
|
if param.type == 'variable':
|
||||||
|
# if HIGH_COMPATIBILITY and param.text.startswith('container_ip'):
|
||||||
|
# if param.optional is True:
|
||||||
|
# param_option_indexes.append(idx)
|
||||||
|
try:
|
||||||
|
param.text = self.paths.get_variable_path(param.text, namespace)
|
||||||
|
except CreoleDictConsistencyError as err:
|
||||||
|
if param.optional is True:
|
||||||
|
param_option_indexes.append(idx)
|
||||||
|
else:
|
||||||
|
raise err
|
||||||
|
param_option_indexes = list(set(param_option_indexes))
|
||||||
|
param_option_indexes.sort(reverse=True)
|
||||||
|
for idx in param_option_indexes:
|
||||||
|
check.param.pop(idx)
|
||||||
|
if not HIGH_COMPATIBILITY and check.param == []:
|
||||||
|
remove_indexes.append(check_idx)
|
||||||
|
remove_indexes.sort(reverse=True)
|
||||||
|
for idx in remove_indexes:
|
||||||
|
del space[idx]
|
||||||
|
variables = {}
|
||||||
|
for index, check in enumerate(space):
|
||||||
|
namespace = check.namespace
|
||||||
|
if HIGH_COMPATIBILITY:
|
||||||
|
if not self.paths.path_is_defined(check.target):
|
||||||
|
continue
|
||||||
|
check.is_in_leadership = self.paths.get_leader(check.target) != None
|
||||||
|
# let's replace the target by the path
|
||||||
|
check.target = self.paths.get_variable_path(check.target, namespace)
|
||||||
|
if check.target not in variables:
|
||||||
|
variables[check.target] = []
|
||||||
|
variables[check.target].append((index, check))
|
||||||
|
# remove check already set for a variable
|
||||||
|
remove_indexes = []
|
||||||
|
for checks in variables.values():
|
||||||
|
names = {}
|
||||||
|
for idx, check in checks:
|
||||||
|
if HIGH_COMPATIBILITY and check.name == 'valid_enum':
|
||||||
|
redefine = True
|
||||||
|
else:
|
||||||
|
redefine = False
|
||||||
|
#redefine = bool(check.redefine)
|
||||||
|
if redefine and check.name in names:
|
||||||
|
remove_indexes.append(names[check.name])
|
||||||
|
del names[check.name]
|
||||||
|
names[check.name] = idx
|
||||||
|
del check.index
|
||||||
|
remove_indexes.sort(reverse=True)
|
||||||
|
for idx in remove_indexes:
|
||||||
|
del space[idx]
|
||||||
|
remove_indexes = []
|
||||||
|
functions = dir(self.eosfunc)
|
||||||
|
functions.extend(['valid_enum', 'valid_in_network', 'valid_differ'])
|
||||||
|
for idx, check in enumerate(space):
|
||||||
|
if not check.name in functions:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot find check function {}').format(check.name))
|
||||||
|
#is_probe = not check.name in self.eosfunc.func_on_zephir_context
|
||||||
|
#if is_probe:
|
||||||
|
# raise CreoleDictConsistencyError(_('cannot have a check with probe function ({})').format(check.name))
|
||||||
|
if check.name == 'valid_enum':
|
||||||
|
proposed_value_type = False
|
||||||
|
remove_params = []
|
||||||
|
for param_idx, param in enumerate(check.param):
|
||||||
|
if hasattr(param, 'name') and param.name == 'checkval':
|
||||||
|
try:
|
||||||
|
proposed_value_type = self.objectspace._convert_boolean(param.text) == False
|
||||||
|
remove_params.append(param_idx)
|
||||||
|
except TypeError as err:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot load checkval value for variable {}: {}').format(check.target, err))
|
||||||
|
if proposed_value_type:
|
||||||
|
# no more supported
|
||||||
|
raise CreoleDictConsistencyError(_('cannot load checkval value for variable {}, no more supported').format(check.target))
|
||||||
|
remove_params.sort(reverse=True)
|
||||||
|
for param_idx in remove_params:
|
||||||
|
del check.param[param_idx]
|
||||||
|
if len(check.param) != 1:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot set more than one param '
|
||||||
|
'for valid_enum for variable {}'
|
||||||
|
'').format(check.target))
|
||||||
|
param = check.param[0]
|
||||||
|
if proposed_value_type:
|
||||||
|
if param.type == 'variable':
|
||||||
|
try:
|
||||||
|
values = self.load_params_in_validenum(param)
|
||||||
|
except NameError as err:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot load value for variable {}: {}').format(check.target, err))
|
||||||
|
add_value = True
|
||||||
|
if HIGH_COMPATIBILITY and check.is_in_leadership:
|
||||||
|
add_value = False
|
||||||
|
if add_value and values:
|
||||||
|
self.force_value[check.target] = values[0]
|
||||||
|
else:
|
||||||
|
if check.target in self.valid_enums:
|
||||||
|
raise CreoleDictConsistencyError(_('valid_enum already set for {}'
|
||||||
|
'').format(check.target))
|
||||||
|
values = self.load_params_in_validenum(param)
|
||||||
|
self.valid_enums[check.target] = {'type': param.type,
|
||||||
|
'values': values}
|
||||||
|
remove_indexes.append(idx)
|
||||||
|
remove_indexes.sort(reverse=True)
|
||||||
|
for idx in remove_indexes:
|
||||||
|
del space[idx]
|
||||||
|
#convert level to "warnings_only" and hidden to "transitive"
|
||||||
|
for check in space:
|
||||||
|
if check.level == 'warning':
|
||||||
|
check.warnings_only = True
|
||||||
|
else:
|
||||||
|
check.warnings_only = False
|
||||||
|
check.level = None
|
||||||
|
if hasattr(check, 'param'):
|
||||||
|
for param in check.param:
|
||||||
|
if not param.hidden is True:
|
||||||
|
check.transitive = False
|
||||||
|
param.hidden = None
|
||||||
|
|
||||||
|
if not self.space.constraints.check:
|
||||||
|
del self.space.constraints.check
|
||||||
|
|
||||||
|
|
||||||
|
def convert_check(self):
|
||||||
|
if not hasattr(self.space, 'constraints') or not hasattr(self.space.constraints, 'check'):
|
||||||
|
return
|
||||||
|
for check in self.space.constraints.check:
|
||||||
|
variable = self.paths.get_variable_obj(check.target)
|
||||||
|
check_ = self.objectspace.check()
|
||||||
|
name = check.name
|
||||||
|
if name == 'valid_differ':
|
||||||
|
name = 'valid_not_equal'
|
||||||
|
elif name == 'valid_network_netmask':
|
||||||
|
params_len = 1
|
||||||
|
if len(check.param) != params_len:
|
||||||
|
raise CreoleDictConsistencyError(_('{} must have {} param').format(name, params_len))
|
||||||
|
elif name == 'valid_ipnetmask':
|
||||||
|
params_len = 1
|
||||||
|
if len(check.param) != params_len:
|
||||||
|
raise CreoleDictConsistencyError(_('{} must have {} param').format(name, params_len))
|
||||||
|
name = 'valid_ip_netmask'
|
||||||
|
elif name == 'valid_broadcast':
|
||||||
|
params_len = 2
|
||||||
|
if len(check.param) != params_len:
|
||||||
|
raise CreoleDictConsistencyError(_('{} must have {} param').format(name, params_len))
|
||||||
|
elif name == 'valid_in_network':
|
||||||
|
params_len = 2
|
||||||
|
if len(check.param) != params_len:
|
||||||
|
raise CreoleDictConsistencyError(_('{} must have {} param').format(name, params_len))
|
||||||
|
check_.name = name
|
||||||
|
check_.warnings_only = check.warnings_only
|
||||||
|
if hasattr(check, 'param'):
|
||||||
|
check_.param = check.param
|
||||||
|
if not hasattr(variable, 'check'):
|
||||||
|
variable.check = []
|
||||||
|
variable.check.append(check_)
|
||||||
|
del self.space.constraints.check
|
||||||
|
|
||||||
|
|
||||||
|
def convert_fill(self, fill_space, space): # pylint: disable=C0111,R0912
|
||||||
|
fills = {}
|
||||||
|
# sort fill/auto by index
|
||||||
|
for idx, fill in enumerate(fill_space):
|
||||||
|
fills[fill.index] = {'idx': idx, 'fill': fill}
|
||||||
|
del fill.index
|
||||||
|
indexes = list(fills.keys())
|
||||||
|
indexes.sort()
|
||||||
|
del_idx = []
|
||||||
|
for idx in indexes:
|
||||||
|
fill = fills[idx]['fill']
|
||||||
|
variable = self.get_variable(fill.target)
|
||||||
|
if hasattr(variable, 'value'):
|
||||||
|
del variable.value
|
||||||
|
namespace = fill.namespace
|
||||||
|
# let's replace the target by the path
|
||||||
|
fill.target = self.paths.get_variable_path(fill.target, namespace)
|
||||||
|
if not fill.name in dir(self.eosfunc):
|
||||||
|
raise CreoleDictConsistencyError(_('cannot find fill function {}').format(fill.name))
|
||||||
|
#is_probe = not fill.name in self.eosfunc.func_on_zephir_context
|
||||||
|
if hasattr(fill, 'param'):
|
||||||
|
for param in fill.param:
|
||||||
|
if param.type not in TYPE_PARAM_FILL:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot use {} type as a param '
|
||||||
|
'in a fill/auto').format(param.type))
|
||||||
|
if param.type == 'eole':
|
||||||
|
param.type = 'variable'
|
||||||
|
param_option_indexes = []
|
||||||
|
for fill_idx, param in enumerate(fill.param):
|
||||||
|
if not hasattr(param, 'text') and \
|
||||||
|
(param.type == 'variable' or param.type == 'number' or \
|
||||||
|
param.type == 'python'):
|
||||||
|
raise CreoleDictConsistencyError(_("All '{}' variables shall be set in "
|
||||||
|
"order to calculate {}").format(
|
||||||
|
param.type,
|
||||||
|
fill.target))
|
||||||
|
# if param.type == 'container':
|
||||||
|
# param.type = 'eole'
|
||||||
|
# param.text = 'container_ip_{}'.format(param.text)
|
||||||
|
if param.type == 'variable':
|
||||||
|
#if is_probe:
|
||||||
|
# raise CreoleDictConsistencyError(_('Function {0} used to calculate {1} '
|
||||||
|
# 'is executed on remote server, '
|
||||||
|
# 'so cannot depends to an '
|
||||||
|
# 'other variable'
|
||||||
|
# ).format(fill.name, fill.target))
|
||||||
|
# if HIGH_COMPATIBILITY and param.text.startswith('container_ip'):
|
||||||
|
# if param.optional is True:
|
||||||
|
# param_option_indexes.append(fill_idx)
|
||||||
|
try:
|
||||||
|
param.text = self.paths.get_variable_path(param.text, namespace)
|
||||||
|
except CreoleDictConsistencyError as err:
|
||||||
|
if param.optional is True:
|
||||||
|
param_option_indexes.append(fill_idx)
|
||||||
|
else:
|
||||||
|
raise err
|
||||||
|
param_option_indexes = list(set(param_option_indexes))
|
||||||
|
param_option_indexes.sort(reverse=True)
|
||||||
|
for param_idx in param_option_indexes:
|
||||||
|
fill.param.pop(param_idx)
|
||||||
|
self.has_calc.append(fill.target)
|
||||||
|
|
||||||
|
#if is_probe:
|
||||||
|
# variable.force_default_on_freeze = False
|
||||||
|
# self.objectspace.probe_variables.append(fill)
|
||||||
|
# del_idx.append(fills[idx]['idx'])
|
||||||
|
del_idx.sort(reverse=True)
|
||||||
|
for idx in del_idx:
|
||||||
|
space.constraints.fill.pop(idx)
|
||||||
|
for fill in space.constraints.fill:
|
||||||
|
variable = self.paths.get_variable_obj(fill.target)
|
||||||
|
value = self.objectspace.value()
|
||||||
|
value.type = 'calculation'
|
||||||
|
value.name = fill.name
|
||||||
|
if hasattr(fill, 'param'):
|
||||||
|
for param in fill.param:
|
||||||
|
if param.hidden is True:
|
||||||
|
param.transitive = False
|
||||||
|
param.hidden = None
|
||||||
|
value.param = fill.param
|
||||||
|
if not hasattr(variable, 'value'):
|
||||||
|
variable.value = []
|
||||||
|
variable.value.append(value)
|
||||||
|
self.force_not_mandatory.append(fill.target)
|
||||||
|
del space.constraints.fill
|
||||||
|
|
||||||
|
def filter_targets(self): # pylint: disable=C0111
|
||||||
|
for condition_idx, condition in enumerate(self.space.constraints.condition):
|
||||||
|
namespace = condition.namespace
|
||||||
|
del_idx = []
|
||||||
|
for idx, target in enumerate(condition.target):
|
||||||
|
if target.type == 'variable':
|
||||||
|
if (hasattr(target, 'optional') and target.optional is True and
|
||||||
|
not self.paths.path_is_defined(target.name)):
|
||||||
|
del_idx.append(idx)
|
||||||
|
continue
|
||||||
|
if condition.source == target.name:
|
||||||
|
raise CreoleDictConsistencyError(_('target name and source name must be different: {}').format(condition.source))
|
||||||
|
target.name = self.paths.get_variable_path(target.name, namespace)
|
||||||
|
elif target.type == 'family':
|
||||||
|
try:
|
||||||
|
target.name = self.paths.get_family_path(target.name, namespace)
|
||||||
|
except KeyError:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot found family {}').format(target.name))
|
||||||
|
del_idx = list(set(del_idx))
|
||||||
|
del_idx.sort(reverse=True)
|
||||||
|
for idx in del_idx:
|
||||||
|
condition.target.pop(idx)
|
||||||
|
|
||||||
|
def filter_condition_servicelist(self):
|
||||||
|
# automatic generation of the service_access lists
|
||||||
|
# and the service_restriction lists from the servicelist
|
||||||
|
for condition in self.space.constraints.condition:
|
||||||
|
if hasattr(condition, 'target'):
|
||||||
|
new_targets = []
|
||||||
|
for target in condition.target:
|
||||||
|
if target.type == 'servicelist':
|
||||||
|
new_target = copy(target)
|
||||||
|
new_target.type = 'service_accesslist'
|
||||||
|
new_target.name = '___auto_{}'.format(new_target.name)
|
||||||
|
new_targets.append(new_target)
|
||||||
|
|
||||||
|
new_target = copy(target)
|
||||||
|
new_target.type = 'service_restrictionlist'
|
||||||
|
new_target.name = '___auto_{}'.format(new_target.name)
|
||||||
|
new_targets.append(new_target)
|
||||||
|
condition.target.extend(new_targets)
|
||||||
|
|
||||||
|
def check_condition_without_target(self):
|
||||||
|
for condition in self.space.constraints.condition:
|
||||||
|
if not hasattr(condition, 'target'):
|
||||||
|
raise CreoleDictConsistencyError(_('target is mandatory in condition'))
|
||||||
|
|
||||||
|
def check_condition_fallback_not_exists(self, fallback_variables, fallback_lists):
|
||||||
|
# a condition with a fallback **and** the source variable doesn't exist
|
||||||
|
remove_conditions = []
|
||||||
|
for idx, condition in enumerate(self.space.constraints.condition):
|
||||||
|
if (hasattr(condition, 'fallback') and condition.fallback is True and
|
||||||
|
not self.paths.path_is_defined(condition.source)):
|
||||||
|
for target in condition.target:
|
||||||
|
if target.type in ['variable', 'family']:
|
||||||
|
name = target.name.split('.')[-1]
|
||||||
|
if target.type == 'variable':
|
||||||
|
variable = self.get_variable(name)
|
||||||
|
else:
|
||||||
|
variable = self.paths.get_family_obj(name)
|
||||||
|
if condition.name in ['disabled_if_in']:
|
||||||
|
variable.disabled = True
|
||||||
|
if condition.name in ['mandatory_if_in']:
|
||||||
|
variable.mandatory = True
|
||||||
|
if condition.name in ['disabled_if_in', 'disabled_if_not_in',
|
||||||
|
'frozen_if_in', 'frozen_if_not_in']:
|
||||||
|
variable.hidden = False
|
||||||
|
if HIGH_COMPATIBILITY:
|
||||||
|
fallback_variables.append(name)
|
||||||
|
else:
|
||||||
|
listname = target.type
|
||||||
|
if not listname.endswith('list'):
|
||||||
|
raise Exception('not yet implemented')
|
||||||
|
listvars = self.objectspace.list_conditions.get(listname,
|
||||||
|
{}).get(target.name)
|
||||||
|
if listvars:
|
||||||
|
for listvar in listvars:
|
||||||
|
try:
|
||||||
|
variable = self.get_variable(listvar)
|
||||||
|
except CreoleDictConsistencyError:
|
||||||
|
variable = self.paths.get_family_obj(listvar)
|
||||||
|
if condition.name in ['disabled_if_in']:
|
||||||
|
variable.disabled = True
|
||||||
|
if condition.name in ['mandatory_if_in']:
|
||||||
|
variable.mandatory = True
|
||||||
|
if condition.name in ['disabled_if_in', 'disabled_if_not_in',
|
||||||
|
'frozen_if_in', 'frozen_if_not_in']:
|
||||||
|
variable.hidden = False
|
||||||
|
fallback_lists.append(listvar)
|
||||||
|
remove_conditions.append(idx)
|
||||||
|
remove_conditions = list(set(remove_conditions))
|
||||||
|
remove_conditions.sort(reverse=True)
|
||||||
|
for idx in remove_conditions:
|
||||||
|
self.space.constraints.condition.pop(idx)
|
||||||
|
|
||||||
|
def convert_xxxlist_to_variable(self, fallback_lists): # pylint: disable=C0111
|
||||||
|
# transform *list to variable or family
|
||||||
|
for condition_idx, condition in enumerate(self.space.constraints.condition):
|
||||||
|
new_targets = []
|
||||||
|
remove_targets = []
|
||||||
|
for target_idx, target in enumerate(condition.target):
|
||||||
|
if target.type not in ['variable', 'family']:
|
||||||
|
listname = target.type
|
||||||
|
if not listname.endswith('list'):
|
||||||
|
raise Exception('not yet implemented')
|
||||||
|
listvars = self.objectspace.list_conditions.get(listname,
|
||||||
|
{}).get(target.name)
|
||||||
|
if listvars:
|
||||||
|
for listvar in listvars:
|
||||||
|
if listvar in fallback_lists:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
variable = self.get_variable(listvar)
|
||||||
|
type_ = 'variable'
|
||||||
|
except CreoleDictConsistencyError:
|
||||||
|
variable = self.paths.get_family_obj(listvar)
|
||||||
|
type_ = 'family'
|
||||||
|
new_target = self.objectspace.target()
|
||||||
|
new_target.type = type_
|
||||||
|
new_target.name = listvar
|
||||||
|
new_target.index = target.index
|
||||||
|
new_targets.append(new_target)
|
||||||
|
remove_targets.append(target_idx)
|
||||||
|
remove_targets = list(set(remove_targets))
|
||||||
|
remove_targets.sort(reverse=True)
|
||||||
|
for target_idx in remove_targets:
|
||||||
|
condition.target.pop(target_idx)
|
||||||
|
condition.target.extend(new_targets)
|
||||||
|
|
||||||
|
def check_condition(self):
|
||||||
|
# if condition.name == 'hidden_if_in':
|
||||||
|
# condition.name = 'disabled_if_in'
|
||||||
|
# elif condition.name == 'hidden_if_not_in':
|
||||||
|
# condition.name = 'disabled_if_not_in'
|
||||||
|
for condition in self.space.constraints.condition:
|
||||||
|
if condition.name not in ['disabled_if_in', 'disabled_if_not_in', 'frozen_if_in', 'auto_frozen_if_in',
|
||||||
|
'frozen_if_not_in', 'mandatory_if_in', 'mandatory_if_not_in']:
|
||||||
|
raise CreoleDictConsistencyError(_('unknown condition {}').format(condition.name))
|
||||||
|
|
||||||
|
def check_params(self):
|
||||||
|
for condition in self.space.constraints.condition:
|
||||||
|
for param in condition.param:
|
||||||
|
if param.type not in TYPE_PARAM_CONDITION:
|
||||||
|
raise CreoleDictConsistencyError(_('cannot use {} type as a param '
|
||||||
|
'in a condition').format(param.type))
|
||||||
|
if param.type == 'eole':
|
||||||
|
param.type = 'variable'
|
||||||
|
|
||||||
|
def check_choice_option_condition(self, force_remove_targets):
|
||||||
|
# remove condition for ChoiceOption that don't have param
|
||||||
|
remove_conditions = []
|
||||||
|
for condition_idx, condition in enumerate(self.space.constraints.condition):
|
||||||
|
namespace = condition.namespace
|
||||||
|
src_variable = self.paths.get_variable_obj(condition.source)
|
||||||
|
condition.source = self.paths.get_variable_path(condition.source, namespace, allow_source=True)
|
||||||
|
valid_enum = None
|
||||||
|
if condition.source in self.valid_enums and \
|
||||||
|
self.valid_enums[condition.source]['type'] == 'string':
|
||||||
|
valid_enum = self.valid_enums[condition.source]['values']
|
||||||
|
if src_variable.type in FORCE_CHOICE:
|
||||||
|
valid_enum = FORCE_CHOICE[src_variable.type]
|
||||||
|
if valid_enum is not None:
|
||||||
|
remove_param = []
|
||||||
|
for param_idx, param in enumerate(condition.param):
|
||||||
|
if param.text not in valid_enum:
|
||||||
|
remove_param.append(param_idx)
|
||||||
|
remove_param.sort(reverse=True)
|
||||||
|
for idx in remove_param:
|
||||||
|
del condition.param[idx]
|
||||||
|
if condition.param == []:
|
||||||
|
for target in condition.target:
|
||||||
|
if target.name.startswith('creole.'):
|
||||||
|
name = target.name.split('.')[-1]
|
||||||
|
else:
|
||||||
|
name = target.name
|
||||||
|
if target.type == 'variable':
|
||||||
|
variable = self.get_variable(name)
|
||||||
|
else:
|
||||||
|
variable = self.paths.get_family_obj(name)
|
||||||
|
if condition.name == 'disabled_if_not_in':
|
||||||
|
variable.disabled = True
|
||||||
|
force_remove_targets.setdefault(condition.name,
|
||||||
|
[]).append(target.name)
|
||||||
|
elif condition.name == 'frozen_if_not_in':
|
||||||
|
variable.hidden = True
|
||||||
|
force_remove_targets.setdefault(condition.name,
|
||||||
|
[]).append(target.name)
|
||||||
|
elif condition.name == 'mandatory_if_not_in':
|
||||||
|
variable.mandatory = True
|
||||||
|
force_remove_targets.setdefault(condition.name,
|
||||||
|
[]).append(target.name)
|
||||||
|
elif HIGH_COMPATIBILITY and condition.name == 'disabled_if_in':
|
||||||
|
variable.hidden = False
|
||||||
|
remove_conditions.append(condition_idx)
|
||||||
|
remove_conditions = list(set(remove_conditions))
|
||||||
|
remove_conditions.sort(reverse=True)
|
||||||
|
for idx in remove_conditions:
|
||||||
|
self.space.constraints.condition.pop(idx)
|
||||||
|
|
||||||
|
def manage_variable_property(self, force_remove_targets, fallback_variables):
|
||||||
|
for condition in self.space.constraints.condition:
|
||||||
|
remove_targets = []
|
||||||
|
#parse each variable and family
|
||||||
|
for target_idx, target in enumerate(condition.target):
|
||||||
|
if target.name in force_remove_targets.get(condition.name, []):
|
||||||
|
remove_targets.append(target_idx)
|
||||||
|
if target.name.startswith('creole.'):
|
||||||
|
name = target.name.split('.')[-1]
|
||||||
|
else:
|
||||||
|
name = target.name
|
||||||
|
if target.type == 'variable':
|
||||||
|
variable = self.get_variable(name)
|
||||||
|
else:
|
||||||
|
variable = self.paths.get_family_obj(name)
|
||||||
|
if name in fallback_variables:
|
||||||
|
remove_targets.append(target_idx)
|
||||||
|
continue
|
||||||
|
if condition.name in ['disabled_if_in', 'disabled_if_not_in',
|
||||||
|
'frozen_if_in', 'frozen_if_not_in']:
|
||||||
|
variable.hidden = False
|
||||||
|
if condition.name in ['mandatory_if_in', 'mandatory_if_not_in']:
|
||||||
|
variable.mandatory = False
|
||||||
|
if HIGH_COMPATIBILITY and condition.name in ['frozen_if_in',
|
||||||
|
'frozen_if_not_in']:
|
||||||
|
self.has_frozen_if_in_condition.append(name)
|
||||||
|
if condition.name in ['mandatory_if_in', 'mandatory_if_not_in']:
|
||||||
|
self.force_not_mandatory.append(target.name)
|
||||||
|
|
||||||
|
remove_targets = list(set(remove_targets))
|
||||||
|
remove_targets.sort(reverse=True)
|
||||||
|
for target_idx in remove_targets:
|
||||||
|
condition.target.pop(target_idx)
|
||||||
|
|
||||||
|
def remove_condition_with_empty_target(self):
|
||||||
|
remove_conditions = []
|
||||||
|
for condition_idx, condition in enumerate(self.space.constraints.condition):
|
||||||
|
if not condition.target:
|
||||||
|
remove_conditions.append(condition_idx)
|
||||||
|
remove_conditions = list(set(remove_conditions))
|
||||||
|
remove_conditions.sort(reverse=True)
|
||||||
|
for idx in remove_conditions:
|
||||||
|
self.space.constraints.condition.pop(idx)
|
||||||
|
|
||||||
|
def filter_condition(self): # pylint: disable=C0111
|
||||||
|
if not hasattr(self.space, 'constraints') or not hasattr(self.space.constraints, 'condition'):
|
||||||
|
return
|
||||||
|
fallback_variables = []
|
||||||
|
fallback_lists = []
|
||||||
|
force_remove_targets = {}
|
||||||
|
self.check_condition()
|
||||||
|
self.check_params()
|
||||||
|
self.check_condition_without_target()
|
||||||
|
self.filter_condition_servicelist()
|
||||||
|
self.check_condition_fallback_not_exists(fallback_variables, fallback_lists)
|
||||||
|
self.filter_targets()
|
||||||
|
self.convert_xxxlist_to_variable(fallback_lists)
|
||||||
|
self.check_choice_option_condition(force_remove_targets)
|
||||||
|
self.manage_variable_property(force_remove_targets, fallback_variables)
|
||||||
|
self.remove_condition_with_empty_target()
|
||||||
|
for condition in self.space.constraints.condition:
|
||||||
|
if condition.name == 'disabled_if_in':
|
||||||
|
actions = ['disabled']
|
||||||
|
inverse = False
|
||||||
|
elif condition.name == 'disabled_if_not_in':
|
||||||
|
actions = ['disabled']
|
||||||
|
inverse = True
|
||||||
|
elif condition.name == 'frozen_if_in':
|
||||||
|
actions = ['frozen', 'hidden', 'force_default_on_freeze']
|
||||||
|
inverse = False
|
||||||
|
elif condition.name == 'frozen_if_not_in':
|
||||||
|
actions = ['frozen', 'hidden', 'force_default_on_freeze']
|
||||||
|
inverse = True
|
||||||
|
elif condition.name == 'mandatory_if_in':
|
||||||
|
actions = ['mandatory']
|
||||||
|
inverse = False
|
||||||
|
elif condition.name == 'mandatory_if_not_in':
|
||||||
|
actions = ['mandatory']
|
||||||
|
inverse = True
|
||||||
|
elif condition.name == 'auto_frozen_if_in':
|
||||||
|
actions = ['auto_frozen']
|
||||||
|
inverse = True
|
||||||
|
for param in condition.param:
|
||||||
|
if hasattr(param, 'text'):
|
||||||
|
param = param.text
|
||||||
|
else:
|
||||||
|
param = None
|
||||||
|
for target in condition.target:
|
||||||
|
if target.name.startswith('creole.'):
|
||||||
|
name = target.name.split('.')[-1]
|
||||||
|
else:
|
||||||
|
name = target.name
|
||||||
|
if target.type == 'variable':
|
||||||
|
variable = self.get_variable(name)
|
||||||
|
else:
|
||||||
|
variable = self.paths.get_family_obj(name)
|
||||||
|
if not hasattr(variable, 'property'):
|
||||||
|
variable.property = []
|
||||||
|
for action in actions:
|
||||||
|
prop = self.objectspace.property_()
|
||||||
|
prop.type = 'calculation'
|
||||||
|
prop.inverse = inverse
|
||||||
|
prop.source = condition.source
|
||||||
|
prop.expected = param
|
||||||
|
prop.name = action
|
||||||
|
variable.property.append(prop)
|
||||||
|
del self.space.constraints.condition
|
@ -1,57 +0,0 @@
|
|||||||
"""Annotate dictionaries
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from .group import GroupAnnotator
|
|
||||||
from .service import ServiceAnnotator
|
|
||||||
from .variable import VariableAnnotator, CONVERT_OPTION
|
|
||||||
from .check import CheckAnnotator
|
|
||||||
from .value import ValueAnnotator
|
|
||||||
from .condition import ConditionAnnotator
|
|
||||||
from .fill import FillAnnotator
|
|
||||||
from .family import FamilyAnnotator, modes
|
|
||||||
from .property import PropertyAnnotator
|
|
||||||
|
|
||||||
class SpaceAnnotator: # pylint: disable=R0903
|
|
||||||
"""Transformations applied on a CreoleObjSpace instance
|
|
||||||
"""
|
|
||||||
def __init__(self, objectspace, eosfunc_file):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
GroupAnnotator(objectspace)
|
|
||||||
ServiceAnnotator(objectspace)
|
|
||||||
VariableAnnotator(objectspace)
|
|
||||||
CheckAnnotator(objectspace,
|
|
||||||
eosfunc_file,
|
|
||||||
)
|
|
||||||
ConditionAnnotator(objectspace)
|
|
||||||
FillAnnotator(objectspace,
|
|
||||||
eosfunc_file,
|
|
||||||
)
|
|
||||||
ValueAnnotator(objectspace)
|
|
||||||
FamilyAnnotator(objectspace)
|
|
||||||
PropertyAnnotator(objectspace)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('SpaceAnnotator', 'CONVERT_OPTION', 'modes')
|
|
@ -1,228 +0,0 @@
|
|||||||
"""Annotate check
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from typing import List, Any
|
|
||||||
|
|
||||||
from .variable import CONVERT_OPTION
|
|
||||||
from .target import TargetAnnotator
|
|
||||||
from .param import ParamAnnotator
|
|
||||||
|
|
||||||
from ..utils import load_modules
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError, display_xmlfiles
|
|
||||||
|
|
||||||
INTERNAL_FUNCTIONS = ['valid_enum', 'valid_in_network', 'valid_differ', 'valid_entier']
|
|
||||||
|
|
||||||
class CheckAnnotator(TargetAnnotator, ParamAnnotator):
|
|
||||||
"""Annotate check
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
eosfunc_file,
|
|
||||||
):
|
|
||||||
if not hasattr(objectspace.space, 'constraints') or \
|
|
||||||
not hasattr(objectspace.space.constraints, 'check'):
|
|
||||||
return
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.let_none = True
|
|
||||||
self.only_variable = True
|
|
||||||
self.functions = dir(load_modules(eosfunc_file))
|
|
||||||
self.functions.extend(INTERNAL_FUNCTIONS)
|
|
||||||
self.target_is_uniq = False
|
|
||||||
self.convert_target(self.objectspace.space.constraints.check)
|
|
||||||
self.convert_param(self.objectspace.space.constraints.check)
|
|
||||||
self.check_check()
|
|
||||||
self.check_valid_enum()
|
|
||||||
self.check_change_warning()
|
|
||||||
self.convert_valid_entier()
|
|
||||||
self.convert_check()
|
|
||||||
del objectspace.space.constraints.check
|
|
||||||
|
|
||||||
def check_check(self): # pylint: disable=R0912
|
|
||||||
"""valid and manage <check>
|
|
||||||
"""
|
|
||||||
remove_indexes = []
|
|
||||||
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
||||||
if not check.name in self.functions:
|
|
||||||
msg = _(f'cannot find check function "{check.name}"')
|
|
||||||
raise DictConsistencyError(msg, 1, check.xmlfiles)
|
|
||||||
if hasattr(check, 'param') and check.param == []:
|
|
||||||
remove_indexes.append(check_idx)
|
|
||||||
remove_indexes.sort(reverse=True)
|
|
||||||
for idx in remove_indexes:
|
|
||||||
del self.objectspace.space.constraints.check[idx]
|
|
||||||
|
|
||||||
def check_valid_enum(self):
|
|
||||||
"""verify valid_enum
|
|
||||||
"""
|
|
||||||
remove_indexes = []
|
|
||||||
for idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
||||||
if check.name != 'valid_enum':
|
|
||||||
continue
|
|
||||||
for target in check.target:
|
|
||||||
if target.name.path in self.objectspace.valid_enums:
|
|
||||||
check_xmlfiles = display_xmlfiles(self.objectspace.valid_enums\
|
|
||||||
[target.name.path]['xmlfiles'])
|
|
||||||
msg = _(f'valid_enum already set in {check_xmlfiles} '
|
|
||||||
f'for "{target.name.name}", you may have forget remove_check')
|
|
||||||
raise DictConsistencyError(msg, 3, check.xmlfiles)
|
|
||||||
if not hasattr(check, 'param'):
|
|
||||||
msg = _(f'param is mandatory for a valid_enum of variable "{target.name.name}"')
|
|
||||||
raise DictConsistencyError(msg, 4, check.xmlfiles)
|
|
||||||
variable_type = target.name.type
|
|
||||||
values = self._set_valid_enum(target.name,
|
|
||||||
check,
|
|
||||||
)
|
|
||||||
if values:
|
|
||||||
if hasattr(target.name, 'value'):
|
|
||||||
# check value
|
|
||||||
self.check_valid_enum_value(target.name, values)
|
|
||||||
else:
|
|
||||||
# no value, set the first choice has default value
|
|
||||||
new_value = self.objectspace.value(check.xmlfiles)
|
|
||||||
new_value.name = values[0]
|
|
||||||
new_value.type = variable_type
|
|
||||||
target.name.value = [new_value]
|
|
||||||
remove_indexes.append(idx)
|
|
||||||
remove_indexes.sort(reverse=True)
|
|
||||||
for idx in remove_indexes:
|
|
||||||
del self.objectspace.space.constraints.check[idx]
|
|
||||||
|
|
||||||
def _set_valid_enum(self,
|
|
||||||
variable,
|
|
||||||
check,
|
|
||||||
) -> List[Any]:
|
|
||||||
# value for choice's variable is mandatory
|
|
||||||
variable.mandatory = True
|
|
||||||
# build choice
|
|
||||||
variable.values = []
|
|
||||||
variable_type = variable.type
|
|
||||||
variable.type = 'choice'
|
|
||||||
|
|
||||||
has_variable = False
|
|
||||||
values = []
|
|
||||||
for param in check.param:
|
|
||||||
if has_variable:
|
|
||||||
msg = _(f'only one "variable" parameter is allowed for valid_enum '
|
|
||||||
f'of variable "{variable.name}"')
|
|
||||||
raise DictConsistencyError(msg, 5, param.xmlfiles)
|
|
||||||
param_type = variable_type
|
|
||||||
if param.type == 'variable':
|
|
||||||
has_variable = True
|
|
||||||
if param.optional is True:
|
|
||||||
msg = _(f'optional parameter in valid_enum for variable "{variable.name}" '
|
|
||||||
f'is not allowed')
|
|
||||||
raise DictConsistencyError(msg, 14, param.xmlfiles)
|
|
||||||
if not param.text.multi:
|
|
||||||
msg = _(f'only multi "variable" parameter is allowed for valid_enum '
|
|
||||||
f'of variable "{variable.name}"')
|
|
||||||
raise DictConsistencyError(msg, 6, param.xmlfiles)
|
|
||||||
param_type = 'calculation'
|
|
||||||
value = param.text
|
|
||||||
elif param.type == 'nil':
|
|
||||||
value = None
|
|
||||||
else:
|
|
||||||
if 'type' in vars(param) and variable_type != param.type:
|
|
||||||
msg = _(f'parameter in valid_enum has incompatible type "{param.type}" '
|
|
||||||
f'with type of the variable "{variable.name}" ("{variable_type}")')
|
|
||||||
raise DictConsistencyError(msg, 7, param.xmlfiles)
|
|
||||||
if hasattr(param, 'text'):
|
|
||||||
try:
|
|
||||||
value = CONVERT_OPTION[variable_type].get('func', str)(param.text)
|
|
||||||
except ValueError as err:
|
|
||||||
msg = _(f'unable to change type of a valid_enum entry "{param.text}" '
|
|
||||||
f'is not a valid "{variable_type}" for "{variable.name}"')
|
|
||||||
raise DictConsistencyError(msg, 13, variable.xmlfiles) from err
|
|
||||||
values.append(value)
|
|
||||||
choice = self.objectspace.choice(variable.xmlfiles)
|
|
||||||
choice.name = value
|
|
||||||
choice.type = param_type
|
|
||||||
variable.values.append(choice)
|
|
||||||
|
|
||||||
if has_variable:
|
|
||||||
return None
|
|
||||||
|
|
||||||
for target in check.target:
|
|
||||||
self.objectspace.valid_enums[target.name.path] = {'type': variable_type,
|
|
||||||
'values': values,
|
|
||||||
'xmlfiles': check.xmlfiles,
|
|
||||||
}
|
|
||||||
return values
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_valid_enum_value(variable,
|
|
||||||
values,
|
|
||||||
) -> None:
|
|
||||||
"""check that values in valid_enum are valid
|
|
||||||
"""
|
|
||||||
for value in variable.value:
|
|
||||||
if value.name not in values:
|
|
||||||
msg = _(f'value "{value.name}" of variable "{variable.name}" is not in list '
|
|
||||||
f'of all expected values ({values})')
|
|
||||||
raise DictConsistencyError(msg, 15, value.xmlfiles)
|
|
||||||
|
|
||||||
def check_change_warning(self):
|
|
||||||
"""convert level to "warnings_only"
|
|
||||||
"""
|
|
||||||
for check in self.objectspace.space.constraints.check:
|
|
||||||
check.warnings_only = check.level == 'warning'
|
|
||||||
check.level = None
|
|
||||||
|
|
||||||
def convert_valid_entier(self) -> None:
|
|
||||||
"""valid and manage <check>
|
|
||||||
"""
|
|
||||||
remove_indexes = []
|
|
||||||
for check_idx, check in enumerate(self.objectspace.space.constraints.check):
|
|
||||||
if not check.name == 'valid_entier':
|
|
||||||
continue
|
|
||||||
remove_indexes.append(check_idx)
|
|
||||||
if not hasattr(check, 'param'):
|
|
||||||
msg = _(f'{check.name} must have, at least, 1 param')
|
|
||||||
raise DictConsistencyError(msg, 17, check.xmlfiles)
|
|
||||||
for param in check.param:
|
|
||||||
if param.type != 'number':
|
|
||||||
msg = _(f'param in "valid_entier" must be an "integer", not "{param.type}"')
|
|
||||||
raise DictConsistencyError(msg, 18, check.xmlfiles)
|
|
||||||
for target in check.target:
|
|
||||||
if param.name == 'mini':
|
|
||||||
target.name.min_number = int(param.text)
|
|
||||||
elif param.name == 'maxi':
|
|
||||||
target.name.max_number = int(param.text)
|
|
||||||
else:
|
|
||||||
msg = _(f'unknown parameter "{param.name}" in check "valid_entier"')
|
|
||||||
raise DictConsistencyError(msg, 19, check.xmlfiles)
|
|
||||||
remove_indexes.sort(reverse=True)
|
|
||||||
for idx in remove_indexes:
|
|
||||||
del self.objectspace.space.constraints.check[idx]
|
|
||||||
|
|
||||||
def convert_check(self) -> None:
|
|
||||||
"""valid and manage <check>
|
|
||||||
"""
|
|
||||||
for check in self.objectspace.space.constraints.check:
|
|
||||||
for target in check.target:
|
|
||||||
if not hasattr(target.name, 'validators'):
|
|
||||||
target.name.validators = []
|
|
||||||
target.name.validators.append(check)
|
|
@ -1,367 +0,0 @@
|
|||||||
"""Annotate condition
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from typing import List, Any
|
|
||||||
|
|
||||||
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
from ..config import Config
|
|
||||||
|
|
||||||
from .target import TargetAnnotator
|
|
||||||
from .param import ParamAnnotator
|
|
||||||
|
|
||||||
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
|
|
||||||
|
|
||||||
|
|
||||||
class ConditionAnnotator(TargetAnnotator, ParamAnnotator):
|
|
||||||
"""Annotate condition
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.force_service_value = {}
|
|
||||||
if hasattr(objectspace.space, 'variables'):
|
|
||||||
self.convert_auto_freeze()
|
|
||||||
if not hasattr(objectspace.space, 'constraints') or \
|
|
||||||
not hasattr(self.objectspace.space.constraints, 'condition'):
|
|
||||||
return
|
|
||||||
self.target_is_uniq = False
|
|
||||||
self.only_variable = False
|
|
||||||
self.convert_target(self.objectspace.space.constraints.condition)
|
|
||||||
self.convert_param(self.objectspace.space.constraints.condition)
|
|
||||||
self.check_source_target()
|
|
||||||
self.check_condition_fallback()
|
|
||||||
self.convert_xxxlist()
|
|
||||||
self.convert_condition_source()
|
|
||||||
self.check_choice_option_condition()
|
|
||||||
self.remove_condition_with_empty_target()
|
|
||||||
self.convert_condition()
|
|
||||||
|
|
||||||
def convert_auto_freeze(self):
|
|
||||||
"""convert auto_freeze
|
|
||||||
only if FREEZE_AUTOFREEZE_VARIABLE == 'oui' this variable is frozen
|
|
||||||
"""
|
|
||||||
for variables in self.objectspace.space.variables.values():
|
|
||||||
for family in variables.family.values():
|
|
||||||
if not hasattr(family, 'variable'):
|
|
||||||
continue
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.leadership):
|
|
||||||
for follower in variable.variable:
|
|
||||||
self._convert_auto_freeze(follower)
|
|
||||||
else:
|
|
||||||
self._convert_auto_freeze(variable)
|
|
||||||
|
|
||||||
def _convert_auto_freeze(self,
|
|
||||||
variable: 'self.objectspace.variable',
|
|
||||||
) -> None:
|
|
||||||
if not variable.auto_freeze:
|
|
||||||
return
|
|
||||||
if variable.namespace != Config['variable_namespace']:
|
|
||||||
msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}"')
|
|
||||||
raise DictConsistencyError(msg, 49, variable.xmlfiles)
|
|
||||||
new_condition = self.objectspace.condition(variable.xmlfiles)
|
|
||||||
new_condition.name = 'auto_frozen_if_not_in'
|
|
||||||
new_condition.namespace = variable.namespace
|
|
||||||
new_condition.source = FREEZE_AUTOFREEZE_VARIABLE
|
|
||||||
new_param = self.objectspace.param(variable.xmlfiles)
|
|
||||||
new_param.text = 'oui'
|
|
||||||
new_condition.param = [new_param]
|
|
||||||
new_target = self.objectspace.target(variable.xmlfiles)
|
|
||||||
new_target.type = 'variable'
|
|
||||||
new_target.name = variable.name
|
|
||||||
new_condition.target = [new_target]
|
|
||||||
if not hasattr(self.objectspace.space, 'constraints'):
|
|
||||||
self.objectspace.space.constraints = self.objectspace.constraints(variable.xmlfiles)
|
|
||||||
if not hasattr(self.objectspace.space.constraints, 'condition'):
|
|
||||||
self.objectspace.space.constraints.condition = []
|
|
||||||
self.objectspace.space.constraints.condition.append(new_condition)
|
|
||||||
|
|
||||||
def check_source_target(self):
|
|
||||||
"""verify that source != target in condition
|
|
||||||
"""
|
|
||||||
for condition in self.objectspace.space.constraints.condition:
|
|
||||||
for target in condition.target:
|
|
||||||
if target.type == 'variable' and \
|
|
||||||
condition.source in [target.name.name, target.name.path]:
|
|
||||||
msg = _('target name and source name must be different: '
|
|
||||||
f'{condition.source}')
|
|
||||||
raise DictConsistencyError(msg, 11, condition.xmlfiles)
|
|
||||||
|
|
||||||
def check_condition_fallback(self):
|
|
||||||
"""a condition with a fallback **and** the source variable doesn't exist
|
|
||||||
"""
|
|
||||||
remove_conditions = []
|
|
||||||
for idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
# fallback
|
|
||||||
if condition.fallback is False or \
|
|
||||||
self.objectspace.paths.path_is_defined(condition.source):
|
|
||||||
continue
|
|
||||||
if condition.name in ['disabled_if_in', 'mandatory_if_in', 'hidden_if_in']:
|
|
||||||
apply_action = not condition.force_condition_on_fallback
|
|
||||||
else:
|
|
||||||
apply_action = condition.force_inverse_condition_on_fallback
|
|
||||||
remove_conditions.append(idx)
|
|
||||||
if apply_action:
|
|
||||||
self.force_actions_to_variable(condition)
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
self.objectspace.space.constraints.condition.pop(idx)
|
|
||||||
|
|
||||||
def force_actions_to_variable(self,
|
|
||||||
condition: 'self.objectspace.condition',
|
|
||||||
) -> None:
|
|
||||||
"""force property to a variable
|
|
||||||
for example disabled_if_not_in => variable.disabled = True
|
|
||||||
"""
|
|
||||||
actions = self.get_actions_from_condition(condition.name)
|
|
||||||
for target in condition.target:
|
|
||||||
main_action = actions[0]
|
|
||||||
if target.type.endswith('list'):
|
|
||||||
self.force_service_value[target.name] = main_action != 'disabled'
|
|
||||||
continue
|
|
||||||
leader_or_var, variables = self._get_family_variables_from_target(target)
|
|
||||||
setattr(leader_or_var, main_action, True)
|
|
||||||
for action in actions[1:]:
|
|
||||||
for variable in variables:
|
|
||||||
setattr(variable, action, True)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_actions_from_condition(condition_name: str) -> List[str]:
|
|
||||||
"""get action's name from a condition
|
|
||||||
"""
|
|
||||||
if condition_name.startswith('hidden_if_'):
|
|
||||||
return ['hidden', 'frozen', 'force_default_on_freeze']
|
|
||||||
if condition_name == 'auto_frozen_if_not_in':
|
|
||||||
return ['auto_frozen']
|
|
||||||
return [condition_name.split('_', 1)[0]]
|
|
||||||
|
|
||||||
def _get_family_variables_from_target(self,
|
|
||||||
target,
|
|
||||||
):
|
|
||||||
if target.type == 'variable':
|
|
||||||
if not self.objectspace.paths.is_leader(target.name.path):
|
|
||||||
return target.name, [target.name]
|
|
||||||
# it's a leader, so apply property to leadership
|
|
||||||
family_name = self.objectspace.paths.get_variable_family_path(target.name.path)
|
|
||||||
family = self.objectspace.paths.get_family(family_name,
|
|
||||||
target.name.namespace,
|
|
||||||
)
|
|
||||||
return family, family.variable
|
|
||||||
# it's a family
|
|
||||||
variable = self.objectspace.paths.get_family(target.name.path,
|
|
||||||
target.namespace,
|
|
||||||
)
|
|
||||||
if hasattr(variable, 'variable'):
|
|
||||||
return variable, list(variable.variable.values())
|
|
||||||
return variable, []
|
|
||||||
|
|
||||||
def convert_xxxlist(self):
|
|
||||||
"""transform *list to variable or family
|
|
||||||
"""
|
|
||||||
fills = {}
|
|
||||||
for condition in self.objectspace.space.constraints.condition:
|
|
||||||
remove_targets = []
|
|
||||||
for target_idx, target in enumerate(condition.target):
|
|
||||||
if target.type.endswith('list'):
|
|
||||||
listvars = self.objectspace.list_conditions.get(target.type,
|
|
||||||
{}).get(target.name)
|
|
||||||
if listvars:
|
|
||||||
self._convert_xxxlist_to_fill(condition,
|
|
||||||
target,
|
|
||||||
listvars,
|
|
||||||
fills,
|
|
||||||
)
|
|
||||||
remove_targets.append(target_idx)
|
|
||||||
remove_targets.sort(reverse=True)
|
|
||||||
for target_idx in remove_targets:
|
|
||||||
condition.target.pop(target_idx)
|
|
||||||
|
|
||||||
def _convert_xxxlist_to_fill(self,
|
|
||||||
condition: 'self.objectspace.condition',
|
|
||||||
target: 'self.objectspace.target',
|
|
||||||
listvars: list,
|
|
||||||
fills: dict,
|
|
||||||
):
|
|
||||||
for listvar in listvars:
|
|
||||||
if target.name in self.force_service_value:
|
|
||||||
listvar.default = self.force_service_value[target.name]
|
|
||||||
continue
|
|
||||||
if listvar.path in fills:
|
|
||||||
fill = fills[listvar.path]
|
|
||||||
or_needed = True
|
|
||||||
for param in fill.param:
|
|
||||||
if hasattr(param, 'name') and \
|
|
||||||
param.name == 'condition_operator':
|
|
||||||
or_needed = False
|
|
||||||
break
|
|
||||||
fill.index += 1
|
|
||||||
else:
|
|
||||||
fill = self.objectspace.fill(target.xmlfiles)
|
|
||||||
new_target = self.objectspace.target(target.xmlfiles)
|
|
||||||
new_target.name = listvar.path
|
|
||||||
fill.target = [new_target]
|
|
||||||
fill.name = 'calc_value'
|
|
||||||
fill.namespace = 'services'
|
|
||||||
fill.index = 0
|
|
||||||
if not hasattr(self.objectspace.space.constraints, 'fill'):
|
|
||||||
self.objectspace.space.constraints.fill = []
|
|
||||||
self.objectspace.space.constraints.fill.append(fill)
|
|
||||||
fills[listvar.path] = fill
|
|
||||||
param1 = self.objectspace.param(target.xmlfiles)
|
|
||||||
param1.text = False
|
|
||||||
param1.type = 'boolean'
|
|
||||||
param2 = self.objectspace.param(target.xmlfiles)
|
|
||||||
param2.name = 'default'
|
|
||||||
param2.text = True
|
|
||||||
param2.type = 'boolean'
|
|
||||||
fill.param = [param1, param2]
|
|
||||||
or_needed = len(condition.param) != 1
|
|
||||||
if len(condition.param) == 1:
|
|
||||||
values = getattr(condition.param[0], 'text', None)
|
|
||||||
else:
|
|
||||||
values = tuple([getattr(param, 'text', None) for param in condition.param])
|
|
||||||
param3 = self.objectspace.param(target.xmlfiles)
|
|
||||||
param3.name = f'condition_{fill.index}'
|
|
||||||
param3.type = 'variable'
|
|
||||||
param3.text = condition.source
|
|
||||||
fill.param.append(param3)
|
|
||||||
param4 = self.objectspace.param(target.xmlfiles)
|
|
||||||
param4.name = f'expected_{fill.index}'
|
|
||||||
param4.text = values
|
|
||||||
fill.param.append(param4)
|
|
||||||
if condition.name != 'disabled_if_in':
|
|
||||||
param5 = self.objectspace.param(target.xmlfiles)
|
|
||||||
param5.name = f'reverse_condition_{fill.index}'
|
|
||||||
param5.text = True
|
|
||||||
param5.type = 'boolean'
|
|
||||||
fill.param.append(param5)
|
|
||||||
if or_needed:
|
|
||||||
param6 = self.objectspace.param(target.xmlfiles)
|
|
||||||
param6.name = 'condition_operator'
|
|
||||||
param6.text = 'OR'
|
|
||||||
fill.param.append(param6)
|
|
||||||
|
|
||||||
def convert_condition_source(self):
|
|
||||||
"""remove condition for ChoiceOption that don't have param
|
|
||||||
"""
|
|
||||||
for condition in self.objectspace.space.constraints.condition:
|
|
||||||
try:
|
|
||||||
condition.source = self.objectspace.paths.get_variable(condition.source)
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if err.errno == 36:
|
|
||||||
msg = _(f'the source "{condition.source}" in condition cannot be a dynamic '
|
|
||||||
f'variable')
|
|
||||||
raise DictConsistencyError(msg, 20, condition.xmlfiles) from err
|
|
||||||
if err.errno == 42:
|
|
||||||
msg = _(f'the source "{condition.source}" in condition is an unknown variable')
|
|
||||||
raise DictConsistencyError(msg, 23, condition.xmlfiles) from err
|
|
||||||
raise err from err # pragma: no cover
|
|
||||||
|
|
||||||
def check_choice_option_condition(self):
|
|
||||||
"""remove condition of ChoiceOption that doesn't match
|
|
||||||
"""
|
|
||||||
remove_conditions = []
|
|
||||||
for condition_idx, condition in enumerate(self.objectspace.space.constraints.condition):
|
|
||||||
if condition.source.path in self.objectspace.valid_enums:
|
|
||||||
valid_enum = self.objectspace.valid_enums[condition.source.path]['values']
|
|
||||||
remove_param = [param_idx for param_idx, param in enumerate(condition.param) \
|
|
||||||
if param.type != 'variable' and param.text not in valid_enum]
|
|
||||||
if not remove_param:
|
|
||||||
continue
|
|
||||||
remove_param.sort(reverse=True)
|
|
||||||
for idx in remove_param:
|
|
||||||
del condition.param[idx]
|
|
||||||
if not condition.param and condition.name.endswith('_if_not_in'):
|
|
||||||
self.force_actions_to_variable(condition)
|
|
||||||
remove_conditions.append(condition_idx)
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
self.objectspace.space.constraints.condition.pop(idx)
|
|
||||||
|
|
||||||
def remove_condition_with_empty_target(self):
|
|
||||||
"""remove condition with empty target
|
|
||||||
"""
|
|
||||||
# optional target are remove, condition could be empty
|
|
||||||
remove_conditions = [condition_idx for condition_idx, condition in \
|
|
||||||
enumerate(self.objectspace.space.constraints.condition) \
|
|
||||||
if not condition.target]
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
self.objectspace.space.constraints.condition.pop(idx)
|
|
||||||
|
|
||||||
def convert_condition(self):
|
|
||||||
"""valid and manage <condition>
|
|
||||||
"""
|
|
||||||
for condition in self.objectspace.space.constraints.condition:
|
|
||||||
actions = self.get_actions_from_condition(condition.name)
|
|
||||||
for param in condition.param:
|
|
||||||
text = getattr(param, 'text', None)
|
|
||||||
for target in condition.target:
|
|
||||||
leader_or_variable, variables = self._get_family_variables_from_target(target)
|
|
||||||
# if option is already disable, do not apply disable_if_in
|
|
||||||
# check only the first action (example of multiple actions:
|
|
||||||
# 'hidden', 'frozen', 'force_default_on_freeze')
|
|
||||||
main_action = actions[0]
|
|
||||||
if getattr(leader_or_variable, main_action, False) is True:
|
|
||||||
continue
|
|
||||||
self.build_property(leader_or_variable,
|
|
||||||
text,
|
|
||||||
condition,
|
|
||||||
main_action,
|
|
||||||
)
|
|
||||||
if isinstance(leader_or_variable, self.objectspace.variable) and \
|
|
||||||
(leader_or_variable.auto_save or leader_or_variable.auto_freeze) and \
|
|
||||||
'force_default_on_freeze' in actions:
|
|
||||||
continue
|
|
||||||
for action in actions[1:]:
|
|
||||||
# other actions are set to the variable or children of family
|
|
||||||
for variable in variables:
|
|
||||||
self.build_property(variable,
|
|
||||||
text,
|
|
||||||
condition,
|
|
||||||
action,
|
|
||||||
)
|
|
||||||
|
|
||||||
def build_property(self,
|
|
||||||
obj,
|
|
||||||
text: Any,
|
|
||||||
condition: 'self.objectspace.condition',
|
|
||||||
action: str,
|
|
||||||
) -> 'self.objectspace.property_':
|
|
||||||
"""build property_ for a condition
|
|
||||||
"""
|
|
||||||
prop = self.objectspace.property_(obj.xmlfiles)
|
|
||||||
prop.type = 'calculation'
|
|
||||||
prop.inverse = condition.name.endswith('_if_not_in')
|
|
||||||
prop.source = condition.source
|
|
||||||
prop.expected = text
|
|
||||||
prop.name = action
|
|
||||||
if not hasattr(obj, 'properties'):
|
|
||||||
obj.properties = []
|
|
||||||
obj.properties.append(prop)
|
|
@ -1,184 +0,0 @@
|
|||||||
"""Annotate family
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
from ..utils import normalize_family
|
|
||||||
|
|
||||||
|
|
||||||
#mode order is important
|
|
||||||
modes_level = ('basic', 'normal', 'expert')
|
|
||||||
|
|
||||||
|
|
||||||
class Mode: # pylint: disable=R0903
|
|
||||||
"""Class to manage mode level
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
name: str,
|
|
||||||
level: int,
|
|
||||||
) -> None:
|
|
||||||
self.name = name
|
|
||||||
self.level = level
|
|
||||||
|
|
||||||
def __gt__(self,
|
|
||||||
other: int,
|
|
||||||
) -> bool:
|
|
||||||
return other.level < self.level
|
|
||||||
|
|
||||||
|
|
||||||
modes = {name: Mode(name, idx) for idx, name in enumerate(modes_level)}
|
|
||||||
|
|
||||||
|
|
||||||
class FamilyAnnotator:
|
|
||||||
"""Annotate family
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
if not hasattr(self.objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
self.remove_empty_families()
|
|
||||||
self.family_names()
|
|
||||||
self.change_modes()
|
|
||||||
self.dynamic_families()
|
|
||||||
self.convert_help()
|
|
||||||
|
|
||||||
def remove_empty_families(self) -> None:
|
|
||||||
"""Remove all families without any variable
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
removed_families = []
|
|
||||||
for family_name, family in families.family.items():
|
|
||||||
if not hasattr(family, 'variable') or len(family.variable) == 0:
|
|
||||||
removed_families.append(family_name)
|
|
||||||
for family_name in removed_families:
|
|
||||||
del families.family[family_name]
|
|
||||||
|
|
||||||
def family_names(self) -> None:
|
|
||||||
"""Set doc, path, ... to family
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
families.doc = families.name
|
|
||||||
families.path = families.name
|
|
||||||
for family in families.family.values():
|
|
||||||
if not hasattr(family, 'description'):
|
|
||||||
family.description = family.name
|
|
||||||
family.doc = family.description
|
|
||||||
del family.description
|
|
||||||
family.name = normalize_family(family.name)
|
|
||||||
|
|
||||||
def change_modes(self):
|
|
||||||
"""change the mode of variables
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
family_mode = family.mode
|
|
||||||
# default is high level
|
|
||||||
new_family_mode = modes_level[-1]
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if not isinstance(variable, self.objectspace.leadership):
|
|
||||||
func = self._change_variabe_mode
|
|
||||||
else:
|
|
||||||
func = self._change_variable_mode_leader
|
|
||||||
func(variable,
|
|
||||||
family_mode,
|
|
||||||
)
|
|
||||||
if modes[new_family_mode] > modes[variable.mode]:
|
|
||||||
new_family_mode = variable.mode
|
|
||||||
# set the lower variable mode to family
|
|
||||||
family.mode = new_family_mode
|
|
||||||
|
|
||||||
def _change_variabe_mode(self,
|
|
||||||
variable,
|
|
||||||
family_mode: str,
|
|
||||||
is_follower=False,
|
|
||||||
) -> None:
|
|
||||||
# auto_save or auto_freeze variable is set to 'basic' mode
|
|
||||||
# if its mode is not defined by the user
|
|
||||||
if 'mode' not in vars(variable) and \
|
|
||||||
(variable.auto_save is True or variable.auto_freeze is True):
|
|
||||||
variable.mode = modes_level[0]
|
|
||||||
# mandatory variable without value is a basic variable
|
|
||||||
if variable.mandatory is True and not hasattr(variable, 'default'):
|
|
||||||
variable.mode = modes_level[0]
|
|
||||||
# none basic variable in high level family has to be in high level
|
|
||||||
if modes[variable.mode] < modes[family_mode] and \
|
|
||||||
(not is_follower or variable.mode != modes_level[0]):
|
|
||||||
variable.mode = family_mode
|
|
||||||
|
|
||||||
def _change_variable_mode_leader(self,
|
|
||||||
leadership,
|
|
||||||
family_mode: str,
|
|
||||||
) -> None:
|
|
||||||
is_follower = False
|
|
||||||
leader_mode = None
|
|
||||||
for follower in leadership.variable:
|
|
||||||
if follower.auto_save is True:
|
|
||||||
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
|
|
||||||
raise DictConsistencyError(msg, 29, leadership.xmlfiles)
|
|
||||||
if follower.auto_freeze is True:
|
|
||||||
msg = f'leader/followers "{follower.name}" could not be auto_freeze'
|
|
||||||
raise DictConsistencyError(_(msg), 30, leadership.xmlfiles)
|
|
||||||
self._change_variabe_mode(follower,
|
|
||||||
family_mode,
|
|
||||||
is_follower,
|
|
||||||
)
|
|
||||||
if leader_mode is None:
|
|
||||||
leader_mode = leadership.variable[0].mode
|
|
||||||
leadership.variable[0].mode = None
|
|
||||||
else:
|
|
||||||
# leader's mode is minimum level
|
|
||||||
if modes[leader_mode] > modes[follower.mode]:
|
|
||||||
follower.mode = leader_mode
|
|
||||||
is_follower = True
|
|
||||||
leadership.mode = leader_mode
|
|
||||||
|
|
||||||
def dynamic_families(self):
|
|
||||||
"""link dynamic families to object
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
if 'dynamic' not in vars(family):
|
|
||||||
continue
|
|
||||||
family.suffixes = self.objectspace.paths.get_variable(family.dynamic)
|
|
||||||
del family.dynamic
|
|
||||||
if not family.suffixes.multi:
|
|
||||||
msg = _(f'dynamic family "{family.name}" must be linked '
|
|
||||||
f'to multi variable')
|
|
||||||
raise DictConsistencyError(msg, 16, family.xmlfiles)
|
|
||||||
|
|
||||||
def convert_help(self):
|
|
||||||
"""Convert variable help
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
if not hasattr(family, 'help'):
|
|
||||||
continue
|
|
||||||
if not hasattr(family, 'information'):
|
|
||||||
family.information = self.objectspace.information(family.xmlfiles)
|
|
||||||
family.information.help = family.help
|
|
||||||
del family.help
|
|
@ -1,75 +0,0 @@
|
|||||||
"""Fill annotator
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from ..utils import load_modules
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
|
|
||||||
from .target import TargetAnnotator
|
|
||||||
from .param import ParamAnnotator
|
|
||||||
|
|
||||||
|
|
||||||
class FillAnnotator(TargetAnnotator, ParamAnnotator):
|
|
||||||
"""Fill annotator
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
eosfunc_file,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
if not hasattr(objectspace.space, 'constraints') or \
|
|
||||||
not hasattr(self.objectspace.space.constraints, 'fill'):
|
|
||||||
return
|
|
||||||
self.functions = dir(load_modules(eosfunc_file))
|
|
||||||
self.target_is_uniq = True
|
|
||||||
self.only_variable = True
|
|
||||||
self.convert_target(self.objectspace.space.constraints.fill)
|
|
||||||
self.convert_param(self.objectspace.space.constraints.fill)
|
|
||||||
self.fill_to_value()
|
|
||||||
del self.objectspace.space.constraints.fill
|
|
||||||
|
|
||||||
def fill_to_value(self) -> None:
|
|
||||||
"""valid and manage <fill>
|
|
||||||
"""
|
|
||||||
for fill in self.objectspace.space.constraints.fill:
|
|
||||||
for target in fill.target:
|
|
||||||
# test if the function exists
|
|
||||||
if fill.name not in self.functions:
|
|
||||||
msg = _(f'cannot find fill function "{fill.name}"')
|
|
||||||
raise DictConsistencyError(msg, 25, fill.xmlfiles)
|
|
||||||
|
|
||||||
# create an object value
|
|
||||||
value = self.objectspace.value(fill.xmlfiles)
|
|
||||||
value.type = 'calculation'
|
|
||||||
value.name = fill.name
|
|
||||||
if target.name.namespace == 'services':
|
|
||||||
target.name.default = value
|
|
||||||
else:
|
|
||||||
target.name.value = [value]
|
|
||||||
|
|
||||||
# manage params
|
|
||||||
if hasattr(fill, 'param') and fill.param:
|
|
||||||
value.param = fill.param
|
|
@ -1,156 +0,0 @@
|
|||||||
"""Annotate group
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
|
|
||||||
|
|
||||||
class GroupAnnotator:
|
|
||||||
"""Annotate group
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
if not hasattr(self.objectspace.space, 'constraints') or \
|
|
||||||
not hasattr(self.objectspace.space.constraints, 'group'):
|
|
||||||
return
|
|
||||||
self.convert_groups()
|
|
||||||
|
|
||||||
def convert_groups(self): # pylint: disable=C0111
|
|
||||||
"""convert groups
|
|
||||||
"""
|
|
||||||
# store old leaders family name
|
|
||||||
cache_paths = {}
|
|
||||||
for group in self.objectspace.space.constraints.group:
|
|
||||||
if group.leader in cache_paths:
|
|
||||||
leader_fam_path = cache_paths[group.leader]
|
|
||||||
else:
|
|
||||||
leader_fam_path = self.objectspace.paths.get_variable_family_path(group.leader)
|
|
||||||
cache_paths[group.leader] = leader_fam_path
|
|
||||||
follower_names = list(group.follower.keys())
|
|
||||||
leader = self.objectspace.paths.get_variable(group.leader)
|
|
||||||
ori_leader_family = self.objectspace.paths.get_family(leader_fam_path,
|
|
||||||
leader.namespace,
|
|
||||||
)
|
|
||||||
has_a_leader = False
|
|
||||||
for variable in list(ori_leader_family.variable.values()):
|
|
||||||
if isinstance(variable, self.objectspace.leadership) and \
|
|
||||||
variable.variable[0].name == leader.name:
|
|
||||||
# append follower to an existed leadership
|
|
||||||
leader_space = variable
|
|
||||||
has_a_leader = True
|
|
||||||
elif variable.name == leader.name:
|
|
||||||
# it's a leader
|
|
||||||
leader_space = self.manage_leader(variable,
|
|
||||||
group,
|
|
||||||
ori_leader_family,
|
|
||||||
)
|
|
||||||
has_a_leader = True
|
|
||||||
elif has_a_leader:
|
|
||||||
# it's should be a follower
|
|
||||||
self.manage_follower(follower_names.pop(0),
|
|
||||||
leader_fam_path,
|
|
||||||
variable,
|
|
||||||
leader_space,
|
|
||||||
)
|
|
||||||
# this variable is not more in ori_leader_family
|
|
||||||
ori_leader_family.variable.pop(variable.name)
|
|
||||||
if follower_names == []:
|
|
||||||
# no more follower
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
joined = '", "'.join(follower_names)
|
|
||||||
msg = _(f'when parsing leadership, we espect to find those followers "{joined}"')
|
|
||||||
raise DictConsistencyError(msg, 31, variable.xmlfiles)
|
|
||||||
del self.objectspace.space.constraints.group
|
|
||||||
|
|
||||||
def manage_leader(self,
|
|
||||||
variable: 'Variable',
|
|
||||||
group: 'Group',
|
|
||||||
ori_leader_family,
|
|
||||||
) -> 'Leadership':
|
|
||||||
"""manage leader's variable
|
|
||||||
"""
|
|
||||||
if variable.multi is not True:
|
|
||||||
msg = _(f'the variable "{variable.name}" in a group must be multi')
|
|
||||||
raise DictConsistencyError(msg, 32, variable.xmlfiles)
|
|
||||||
if hasattr(group, 'name'):
|
|
||||||
leadership_name = group.name
|
|
||||||
else:
|
|
||||||
leadership_name = variable.name
|
|
||||||
leader_space = self.objectspace.leadership(variable.xmlfiles)
|
|
||||||
leader_space.variable = []
|
|
||||||
leader_space.name = leadership_name
|
|
||||||
leader_space.hidden = variable.hidden
|
|
||||||
if variable.hidden:
|
|
||||||
variable.frozen = True
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
variable.hidden = None
|
|
||||||
if hasattr(group, 'description'):
|
|
||||||
leader_space.doc = group.description
|
|
||||||
elif variable.name == leadership_name and hasattr(variable, 'description'):
|
|
||||||
leader_space.doc = variable.description
|
|
||||||
else:
|
|
||||||
leader_space.doc = leadership_name
|
|
||||||
leadership_path = ori_leader_family.path + '.' + leadership_name
|
|
||||||
self.objectspace.paths.add_leadership(variable.namespace,
|
|
||||||
leadership_path,
|
|
||||||
leader_space,
|
|
||||||
)
|
|
||||||
leader_family = self.objectspace.paths.get_family(ori_leader_family.path,
|
|
||||||
ori_leader_family.namespace,
|
|
||||||
)
|
|
||||||
leader_family.variable[variable.name] = leader_space
|
|
||||||
leader_space.variable.append(variable)
|
|
||||||
self.objectspace.paths.set_leader(variable.namespace,
|
|
||||||
ori_leader_family.path,
|
|
||||||
leadership_name,
|
|
||||||
variable.name,
|
|
||||||
)
|
|
||||||
return leader_space
|
|
||||||
|
|
||||||
def manage_follower(self,
|
|
||||||
follower_name: str,
|
|
||||||
leader_family_name: str,
|
|
||||||
variable: 'Variable',
|
|
||||||
leader_space: 'Leadership',
|
|
||||||
) -> None:
|
|
||||||
"""manage follower
|
|
||||||
"""
|
|
||||||
if variable.name != follower_name:
|
|
||||||
msg = _('when parsing leadership, we expect to find the follower '
|
|
||||||
f'"{follower_name}" but we found "{variable.name}"')
|
|
||||||
raise DictConsistencyError(msg, 33, variable.xmlfiles)
|
|
||||||
self.objectspace.paths.set_leader(variable.namespace,
|
|
||||||
leader_family_name,
|
|
||||||
leader_space.name,
|
|
||||||
variable.name,
|
|
||||||
)
|
|
||||||
if leader_space.hidden:
|
|
||||||
variable.frozen = True
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
leader_space.variable.append(variable)
|
|
@ -1,74 +0,0 @@
|
|||||||
"""Param annotator
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
|
|
||||||
|
|
||||||
class ParamAnnotator:
|
|
||||||
def convert_param(self, objects) -> None:
|
|
||||||
""" valid and convert param
|
|
||||||
"""
|
|
||||||
for obj in objects:
|
|
||||||
if not hasattr(obj, 'param'):
|
|
||||||
continue
|
|
||||||
param_to_delete = []
|
|
||||||
for param_idx, param in enumerate(obj.param):
|
|
||||||
if param.type == 'suffix':
|
|
||||||
if hasattr(param, 'text'):
|
|
||||||
msg = _(f'"{param.type}" parameter must not have a value')
|
|
||||||
raise DictConsistencyError(msg, 28, obj.xmlfiles)
|
|
||||||
for target in obj.target:
|
|
||||||
if not self.objectspace.paths.variable_is_dynamic(target.name.path):
|
|
||||||
msg = _(f'"suffix" parameter cannot be set with target "{target.name}"'
|
|
||||||
f' which is not a dynamic variable')
|
|
||||||
raise DictConsistencyError(msg, 53, obj.xmlfiles)
|
|
||||||
elif not hasattr(param, 'text'):
|
|
||||||
if not param.type == 'nil':
|
|
||||||
msg = _(f'"{param.type}" parameter must have a value')
|
|
||||||
raise DictConsistencyError(msg, 27, obj.xmlfiles)
|
|
||||||
elif param.type == 'nil':
|
|
||||||
msg = _(f'"{param.type}" parameter must not have a value')
|
|
||||||
raise DictConsistencyError(msg, 40, obj.xmlfiles)
|
|
||||||
if param.type == 'variable':
|
|
||||||
try:
|
|
||||||
path, suffix = self.objectspace.paths.get_variable_path(param.text,
|
|
||||||
obj.namespace,
|
|
||||||
)
|
|
||||||
param.text = self.objectspace.paths.get_variable(path)
|
|
||||||
if suffix:
|
|
||||||
param.suffix = suffix
|
|
||||||
family_path = self.objectspace.paths.get_variable_family_path(path)
|
|
||||||
param.family = self.objectspace.paths.get_family(family_path,
|
|
||||||
param.text.namespace,
|
|
||||||
)
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if err.errno != 42 or not param.optional:
|
|
||||||
raise err
|
|
||||||
param_to_delete.append(param_idx)
|
|
||||||
param_to_delete.sort(reverse=True)
|
|
||||||
for param_idx in param_to_delete:
|
|
||||||
obj.param.pop(param_idx)
|
|
@ -1,106 +0,0 @@
|
|||||||
"""Annotate properties
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
|
|
||||||
|
|
||||||
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
|
|
||||||
'force_store_value', 'disabled', 'mandatory')
|
|
||||||
CONVERT_PROPERTIES = {'auto_save': ['force_store_value'],
|
|
||||||
'auto_freeze': ['force_store_value', 'auto_freeze'],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PropertyAnnotator:
|
|
||||||
"""Annotate properties
|
|
||||||
"""
|
|
||||||
def __init__(self, objectspace):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
if hasattr(self.objectspace.space, 'services'):
|
|
||||||
self.convert_services()
|
|
||||||
if hasattr(self.objectspace.space, 'variables'):
|
|
||||||
self.convert_variables()
|
|
||||||
|
|
||||||
def convert_property(self,
|
|
||||||
variable,
|
|
||||||
) -> None:
|
|
||||||
"""convert properties
|
|
||||||
"""
|
|
||||||
# hidden variable is also frozen
|
|
||||||
if isinstance(variable, self.objectspace.variable) and variable.hidden is True:
|
|
||||||
variable.frozen = True
|
|
||||||
if not variable.auto_save and \
|
|
||||||
not variable.auto_freeze and \
|
|
||||||
'force_default_on_freeze' not in vars(variable):
|
|
||||||
variable.force_default_on_freeze = True
|
|
||||||
if not hasattr(variable, 'properties'):
|
|
||||||
variable.properties = []
|
|
||||||
for prop in PROPERTIES:
|
|
||||||
if hasattr(variable, prop):
|
|
||||||
if getattr(variable, prop) is True:
|
|
||||||
for subprop in CONVERT_PROPERTIES.get(prop, [prop]):
|
|
||||||
variable.properties.append(subprop)
|
|
||||||
setattr(variable, prop, None)
|
|
||||||
if hasattr(variable, 'mode') and variable.mode:
|
|
||||||
variable.properties.append(variable.mode)
|
|
||||||
variable.mode = None
|
|
||||||
if 'force_store_value' in variable.properties and \
|
|
||||||
'force_default_on_freeze' in variable.properties: # pragma: no cover
|
|
||||||
# should not appened
|
|
||||||
msg = _('cannot have auto_freeze or auto_store with the hidden '
|
|
||||||
f'variable "{variable.name}"')
|
|
||||||
raise DictConsistencyError(msg, 50, variable.xmlfiles)
|
|
||||||
if not variable.properties:
|
|
||||||
del variable.properties
|
|
||||||
|
|
||||||
def convert_services(self) -> None:
|
|
||||||
"""convert services
|
|
||||||
"""
|
|
||||||
self.convert_property(self.objectspace.space.services)
|
|
||||||
for services in self.objectspace.space.services.service.values():
|
|
||||||
self.convert_property(services)
|
|
||||||
for service in vars(services).values():
|
|
||||||
if not isinstance(service, self.objectspace.family):
|
|
||||||
continue
|
|
||||||
self.convert_property(service)
|
|
||||||
for family in service.family:
|
|
||||||
self.convert_property(family)
|
|
||||||
for variable in family.variable:
|
|
||||||
self.convert_property(variable)
|
|
||||||
|
|
||||||
def convert_variables(self) -> None:
|
|
||||||
"""convert variables
|
|
||||||
"""
|
|
||||||
for variables in self.objectspace.space.variables.values():
|
|
||||||
for family in variables.family.values():
|
|
||||||
self.convert_property(family)
|
|
||||||
for variable in family.variable.values():
|
|
||||||
self.convert_property(variable)
|
|
||||||
if not isinstance(variable, self.objectspace.leadership):
|
|
||||||
continue
|
|
||||||
for follower in variable.variable:
|
|
||||||
self.convert_property(follower)
|
|
@ -1,257 +0,0 @@
|
|||||||
"""Annotate services
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from os.path import basename
|
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
from ..i18n import _
|
|
||||||
from ..utils import normalize_family
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
# a CreoleObjSpace's attribute has some annotations
|
|
||||||
# that shall not be present in the exported (flatened) XML
|
|
||||||
ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace',
|
|
||||||
'remove_condition', 'path', 'instance_mode', 'index',
|
|
||||||
'level', 'remove_fill', 'xmlfiles', 'type', 'reflector_name',
|
|
||||||
'reflector_object', 'manage')
|
|
||||||
|
|
||||||
|
|
||||||
KEY_TYPE = {'variable': 'symlink',
|
|
||||||
'PortOption': 'port',
|
|
||||||
'UnicodeOption': 'string',
|
|
||||||
'NetworkOption': 'network',
|
|
||||||
'NetmaskOption': 'netmask',
|
|
||||||
'URLOption': 'web_address',
|
|
||||||
'FilenameOption': 'filename',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceAnnotator:
|
|
||||||
"""Manage service's object
|
|
||||||
for example::
|
|
||||||
<services>
|
|
||||||
<service name="test">
|
|
||||||
<service_access service='ntp'>
|
|
||||||
<port protocol='udp' service_accesslist='ntp_udp'>123</port>
|
|
||||||
</service_access>
|
|
||||||
</service>
|
|
||||||
</services>
|
|
||||||
"""
|
|
||||||
def __init__(self, objectspace):
|
|
||||||
self.objectspace = objectspace
|
|
||||||
if hasattr(self.objectspace.space, 'services'):
|
|
||||||
if not hasattr(self.objectspace.space.services, 'service'):
|
|
||||||
del self.objectspace.space.services
|
|
||||||
else:
|
|
||||||
self.convert_services()
|
|
||||||
|
|
||||||
def convert_services(self):
|
|
||||||
"""convert services to variables
|
|
||||||
"""
|
|
||||||
self.objectspace.space.services.hidden = True
|
|
||||||
self.objectspace.space.services.name = 'services'
|
|
||||||
self.objectspace.space.services.doc = 'services'
|
|
||||||
self.objectspace.space.services.path = 'services'
|
|
||||||
for service_name, service in self.objectspace.space.services.service.items():
|
|
||||||
service.information = self.objectspace.information(service.xmlfiles)
|
|
||||||
service.information.manage = service.manage
|
|
||||||
service.manage = None
|
|
||||||
for elttype, values in dict(vars(service)).items():
|
|
||||||
if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES:
|
|
||||||
continue
|
|
||||||
eltname = elttype + 's'
|
|
||||||
path = '.'.join(['services', normalize_family(service_name), eltname])
|
|
||||||
family = self._gen_family(eltname,
|
|
||||||
path,
|
|
||||||
service.xmlfiles,
|
|
||||||
)
|
|
||||||
if isinstance(values, dict):
|
|
||||||
values = list(values.values())
|
|
||||||
family.family = self.make_group_from_elts(service_name,
|
|
||||||
elttype,
|
|
||||||
values,
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
setattr(service, elttype, family)
|
|
||||||
service.doc = service.name
|
|
||||||
|
|
||||||
def make_group_from_elts(self,
|
|
||||||
service_name,
|
|
||||||
elttype,
|
|
||||||
elts,
|
|
||||||
path,
|
|
||||||
):
|
|
||||||
"""Splits each objects into a group (and `OptionDescription`, in tiramisu terms)
|
|
||||||
and build elements and its attributes (the `Options` in tiramisu terms)
|
|
||||||
"""
|
|
||||||
families = []
|
|
||||||
listname = '{}list'.format(elttype)
|
|
||||||
for elt in elts:
|
|
||||||
# try to launch _update_xxxx() function
|
|
||||||
update_elt = '_update_' + elttype
|
|
||||||
if hasattr(self, update_elt):
|
|
||||||
getattr(self, update_elt)(elt,
|
|
||||||
service_name,
|
|
||||||
)
|
|
||||||
c_name, subpath = self._get_name_path(elt,
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
family = self._gen_family(c_name,
|
|
||||||
subpath,
|
|
||||||
elt.xmlfiles,
|
|
||||||
)
|
|
||||||
family.variable = []
|
|
||||||
activate_obj = self._generate_element('boolean',
|
|
||||||
'activate',
|
|
||||||
True,
|
|
||||||
elt.xmlfiles,
|
|
||||||
'.'.join([subpath, 'activate']),
|
|
||||||
)
|
|
||||||
for key in dir(elt):
|
|
||||||
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
|
|
||||||
continue
|
|
||||||
value = getattr(elt, key)
|
|
||||||
if key == listname:
|
|
||||||
self.objectspace.list_conditions.setdefault(listname,
|
|
||||||
{}).setdefault(
|
|
||||||
value,
|
|
||||||
[]).append(activate_obj)
|
|
||||||
continue
|
|
||||||
family.variable.append(self._generate_element(self._get_type(key,
|
|
||||||
elttype,
|
|
||||||
elt,
|
|
||||||
),
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
elt.xmlfiles,
|
|
||||||
f'{subpath}.{key}'
|
|
||||||
))
|
|
||||||
family.variable.append(activate_obj)
|
|
||||||
families.append(family)
|
|
||||||
return families
|
|
||||||
|
|
||||||
def _get_name_path(self,
|
|
||||||
elt,
|
|
||||||
path: str,
|
|
||||||
) -> Tuple[str, str]:
|
|
||||||
# create element name, if already exists, add _xx to be uniq
|
|
||||||
if hasattr(elt, 'source'):
|
|
||||||
name = elt.source
|
|
||||||
else:
|
|
||||||
name = elt.name
|
|
||||||
idx = 0
|
|
||||||
while True:
|
|
||||||
c_name = name
|
|
||||||
if idx:
|
|
||||||
c_name += f'_{idx}'
|
|
||||||
subpath = '{}.{}'.format(path, normalize_family(c_name))
|
|
||||||
try:
|
|
||||||
self.objectspace.paths.get_family(subpath, 'services')
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if err.errno == 42:
|
|
||||||
return c_name, subpath
|
|
||||||
idx += 1
|
|
||||||
|
|
||||||
def _gen_family(self,
|
|
||||||
name,
|
|
||||||
path,
|
|
||||||
xmlfiles
|
|
||||||
):
|
|
||||||
family = self.objectspace.family(xmlfiles)
|
|
||||||
family.name = normalize_family(name)
|
|
||||||
family.doc = name
|
|
||||||
family.mode = None
|
|
||||||
self.objectspace.paths.add_family('services',
|
|
||||||
path,
|
|
||||||
family,
|
|
||||||
)
|
|
||||||
return family
|
|
||||||
|
|
||||||
def _generate_element(self,
|
|
||||||
type_,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
xmlfiles,
|
|
||||||
path,
|
|
||||||
): # pylint: disable=R0913
|
|
||||||
variable = self.objectspace.variable(xmlfiles)
|
|
||||||
variable.name = normalize_family(key)
|
|
||||||
variable.mode = None
|
|
||||||
variable.type = type_
|
|
||||||
if type_ == 'symlink':
|
|
||||||
variable.opt = self.objectspace.paths.get_variable(value)
|
|
||||||
variable.multi = None
|
|
||||||
else:
|
|
||||||
variable.doc = key
|
|
||||||
variable.default = value
|
|
||||||
variable.namespace = 'services'
|
|
||||||
self.objectspace.paths.add_variable('services',
|
|
||||||
path,
|
|
||||||
'service',
|
|
||||||
False,
|
|
||||||
variable,
|
|
||||||
)
|
|
||||||
return variable
|
|
||||||
|
|
||||||
def _get_type(self,
|
|
||||||
key: str,
|
|
||||||
elt_name: str,
|
|
||||||
elt,
|
|
||||||
) -> str:
|
|
||||||
if key == 'name':
|
|
||||||
dtd_key_type = elt_name + '_type'
|
|
||||||
else:
|
|
||||||
dtd_key_type = key + '_type'
|
|
||||||
if key in self.objectspace.booleans_attributs:
|
|
||||||
return 'boolean'
|
|
||||||
if hasattr(elt, dtd_key_type):
|
|
||||||
return KEY_TYPE[getattr(elt, dtd_key_type)]
|
|
||||||
return 'string'
|
|
||||||
|
|
||||||
def _update_override(self,
|
|
||||||
file_,
|
|
||||||
service_name,
|
|
||||||
):
|
|
||||||
file_.name = f'/systemd/system/{service_name}.service.d/rougail.conf'
|
|
||||||
# retrieve default value from File object
|
|
||||||
for attr in ['owner', 'group', 'mode']:
|
|
||||||
setattr(file_, attr, getattr(self.objectspace.file, attr))
|
|
||||||
if not hasattr(file_, 'source'):
|
|
||||||
file_.source = f'{service_name}.service'
|
|
||||||
self._update_file(file_,
|
|
||||||
service_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _update_file(self,
|
|
||||||
file_,
|
|
||||||
service_name,
|
|
||||||
):
|
|
||||||
if not hasattr(file_, 'file_type') or file_.file_type == "UnicodeOption":
|
|
||||||
if not hasattr(file_, 'source'):
|
|
||||||
file_.source = basename(file_.name)
|
|
||||||
elif not hasattr(file_, 'source'):
|
|
||||||
msg = _(f'attribute "source" is mandatory for the file "{file_.name}" '
|
|
||||||
f'"({service_name})"')
|
|
||||||
raise DictConsistencyError(msg, 34, file_.xmlfiles)
|
|
@ -1,82 +0,0 @@
|
|||||||
"""Target annotator
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from ..i18n import _
|
|
||||||
from ..error import DictConsistencyError
|
|
||||||
|
|
||||||
|
|
||||||
class TargetAnnotator:
|
|
||||||
def convert_target(self,
|
|
||||||
objects,
|
|
||||||
) -> None:
|
|
||||||
""" valid and convert param
|
|
||||||
"""
|
|
||||||
targets = []
|
|
||||||
for obj in objects:
|
|
||||||
if not hasattr(obj, 'target'):
|
|
||||||
msg = _('target is mandatory')
|
|
||||||
raise DictConsistencyError(msg, 9, obj.xmlfiles)
|
|
||||||
remove_targets = []
|
|
||||||
for index, target in enumerate(obj.target):
|
|
||||||
# test if it's redefined calculation
|
|
||||||
if self.target_is_uniq and target.name in targets:
|
|
||||||
msg = _(f'A fill already exists for the target of "{target.name}" created')
|
|
||||||
raise DictConsistencyError(msg, 24, obj.xmlfiles)
|
|
||||||
targets.append(target.name)
|
|
||||||
|
|
||||||
# let's replace the target by the path
|
|
||||||
try:
|
|
||||||
if target.type == 'variable':
|
|
||||||
path, suffix = self.objectspace.paths.get_variable_path(target.name,
|
|
||||||
obj.namespace,
|
|
||||||
)
|
|
||||||
target.name = self.objectspace.paths.get_variable(path)
|
|
||||||
if suffix:
|
|
||||||
msg = _(f'target to {target.name.path} with suffix is not allowed')
|
|
||||||
raise DictConsistencyError(msg, 35, obj.xmlfiles)
|
|
||||||
elif self.only_variable:
|
|
||||||
msg = _(f'target to "{target.name}" with param type "{target.type}" '
|
|
||||||
f'is not allowed')
|
|
||||||
raise DictConsistencyError(msg, 2, obj.xmlfiles)
|
|
||||||
if target.type == 'family':
|
|
||||||
target.name = self.objectspace.paths.get_family(target.name,
|
|
||||||
obj.namespace,
|
|
||||||
)
|
|
||||||
elif target.type.endswith('list') and \
|
|
||||||
obj.name not in ['disabled_if_in', 'disabled_if_not_in']:
|
|
||||||
msg = _(f'target "{target.type}" not allow')
|
|
||||||
raise DictConsistencyError(msg, 10, target.xmlfiles)
|
|
||||||
except DictConsistencyError as err:
|
|
||||||
if err.errno != 42:
|
|
||||||
raise err
|
|
||||||
# for optional variable
|
|
||||||
if not target.optional:
|
|
||||||
msg = f'cannot found target "{target.name}"'
|
|
||||||
raise DictConsistencyError(_(msg), 12, target.xmlfiles) from err
|
|
||||||
remove_targets.append(index)
|
|
||||||
remove_targets.sort(reverse=True)
|
|
||||||
for index in remove_targets:
|
|
||||||
obj.target.pop(index)
|
|
@ -1,95 +0,0 @@
|
|||||||
"""Annotate value
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
|
|
||||||
class ValueAnnotator: # pylint: disable=R0903
|
|
||||||
"""Annotate value
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
) -> None:
|
|
||||||
if not hasattr(objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.convert_value()
|
|
||||||
|
|
||||||
def convert_value(self) -> None:
|
|
||||||
"""convert value
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
if not hasattr(family, 'variable'):
|
|
||||||
continue
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.leadership):
|
|
||||||
variable_type = 'leader'
|
|
||||||
for follower in variable.variable:
|
|
||||||
self._convert_value(follower,
|
|
||||||
variable_type,
|
|
||||||
)
|
|
||||||
variable_type = 'follower'
|
|
||||||
else:
|
|
||||||
self._convert_value(variable)
|
|
||||||
|
|
||||||
def _convert_value(self,
|
|
||||||
variable,
|
|
||||||
variable_type: str=None,
|
|
||||||
) -> None:
|
|
||||||
# a boolean must have value, the default value is "True"
|
|
||||||
if not hasattr(variable, 'value') and variable.type == 'boolean':
|
|
||||||
new_value = self.objectspace.value(variable.xmlfiles)
|
|
||||||
new_value.name = True
|
|
||||||
new_value.type = 'boolean'
|
|
||||||
variable.value = [new_value]
|
|
||||||
"""if the variable is mandatory and doesn't have any value
|
|
||||||
then the variable's mode is set to 'basic'
|
|
||||||
"""
|
|
||||||
# variable with default value is mandatory
|
|
||||||
if hasattr(variable, 'value') and variable.value:
|
|
||||||
has_value = True
|
|
||||||
for value in variable.value:
|
|
||||||
if value.type == 'calculation':
|
|
||||||
has_value = False
|
|
||||||
break
|
|
||||||
if has_value:
|
|
||||||
# if has value without any calculation
|
|
||||||
variable.mandatory = True
|
|
||||||
if not hasattr(variable, 'value'):
|
|
||||||
return
|
|
||||||
if variable.value[0].type == 'calculation':
|
|
||||||
variable.default = variable.value[0]
|
|
||||||
else:
|
|
||||||
if variable.multi:
|
|
||||||
if variable_type != 'follower':
|
|
||||||
variable.default = [value.name for value in variable.value]
|
|
||||||
if variable_type != 'leader':
|
|
||||||
if variable.multi == 'submulti':
|
|
||||||
variable.default_multi = [value.name for value in variable.value]
|
|
||||||
else:
|
|
||||||
variable.default_multi = variable.value[0].name
|
|
||||||
else:
|
|
||||||
variable.default = variable.value[0].name
|
|
||||||
del variable.value
|
|
@ -1,212 +0,0 @@
|
|||||||
"""Annotate variable
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
|
|
||||||
'float': dict(opttype="FloatOption", func=float),
|
|
||||||
'choice': dict(opttype="ChoiceOption"),
|
|
||||||
'string': dict(opttype="StrOption"),
|
|
||||||
'password': dict(opttype="PasswordOption"),
|
|
||||||
'mail': dict(opttype="EmailOption"),
|
|
||||||
'boolean': dict(opttype="BoolOption"),
|
|
||||||
'symlink': dict(opttype="SymLinkOption"),
|
|
||||||
'filename': dict(opttype="FilenameOption"),
|
|
||||||
'date': dict(opttype="DateOption"),
|
|
||||||
'unix_user': dict(opttype="UsernameOption"),
|
|
||||||
'ip': dict(opttype="IPOption", initkwargs={'allow_reserved': True}),
|
|
||||||
'local_ip': dict(opttype="IPOption", initkwargs={'private_only': True,
|
|
||||||
'warnings_only': True}),
|
|
||||||
'netmask': dict(opttype="NetmaskOption"),
|
|
||||||
'network': dict(opttype="NetworkOption"),
|
|
||||||
'broadcast': dict(opttype="BroadcastOption"),
|
|
||||||
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios',
|
|
||||||
'warnings_only': True}),
|
|
||||||
'domain': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname',
|
|
||||||
'allow_ip': False}),
|
|
||||||
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname',
|
|
||||||
'allow_ip': False}),
|
|
||||||
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': True,
|
|
||||||
'allow_without_dot': True}),
|
|
||||||
'port': dict(opttype="PortOption", initkwargs={'allow_private': True}),
|
|
||||||
'mac': dict(opttype="MACOption"),
|
|
||||||
'cidr': dict(opttype="IPOption", initkwargs={'cidr': True}),
|
|
||||||
'network_cidr': dict(opttype="NetworkOption", initkwargs={'cidr': True}),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FORCE_CHOICE = {'schedule': ['none', 'daily', 'weekly', 'monthly'],
|
|
||||||
'schedulemod': ['pre', 'post'],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VariableAnnotator: # pylint: disable=R0903
|
|
||||||
"""Annotate variable
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
if not hasattr(objectspace.space, 'variables'):
|
|
||||||
return
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.convert_variable()
|
|
||||||
self.convert_test()
|
|
||||||
self.convert_help()
|
|
||||||
|
|
||||||
def convert_variable(self):
|
|
||||||
"""convert variable
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
if not hasattr(family, 'variable'):
|
|
||||||
continue
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.leadership):
|
|
||||||
# first variable is a leader, others are follower
|
|
||||||
variable_type = 'leader'
|
|
||||||
for follower in variable.variable:
|
|
||||||
self._convert_variable(families.name,
|
|
||||||
follower,
|
|
||||||
variable_type,
|
|
||||||
)
|
|
||||||
variable_type = 'follower'
|
|
||||||
else:
|
|
||||||
self._convert_variable(families.name,
|
|
||||||
variable,
|
|
||||||
'variable',
|
|
||||||
)
|
|
||||||
|
|
||||||
def _convert_variable(self,
|
|
||||||
namespace: str,
|
|
||||||
variable,
|
|
||||||
variable_type: str,
|
|
||||||
) -> None:
|
|
||||||
if variable.type != 'symlink' and not hasattr(variable, 'description'):
|
|
||||||
variable.description = variable.name
|
|
||||||
if hasattr(variable, 'value'):
|
|
||||||
value_to_del = []
|
|
||||||
for idx, value in enumerate(variable.value):
|
|
||||||
if not hasattr(value, 'name'):
|
|
||||||
value_to_del.append(idx)
|
|
||||||
else:
|
|
||||||
if not hasattr(value, 'type'):
|
|
||||||
value.type = variable.type
|
|
||||||
value.name = CONVERT_OPTION.get(value.type, {}).get('func', str)(value.name)
|
|
||||||
value_to_del.sort(reverse=True)
|
|
||||||
for idx in value_to_del:
|
|
||||||
del variable.value[idx]
|
|
||||||
if not variable.value:
|
|
||||||
del variable.value
|
|
||||||
variable.doc = variable.description
|
|
||||||
del variable.description
|
|
||||||
if variable_type == 'follower':
|
|
||||||
if variable.multi is True:
|
|
||||||
variable.multi = 'submulti'
|
|
||||||
else:
|
|
||||||
variable.multi = True
|
|
||||||
self._convert_valid_enum(namespace,
|
|
||||||
variable,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _convert_valid_enum(self,
|
|
||||||
namespace,
|
|
||||||
variable,
|
|
||||||
):
|
|
||||||
"""some types are, in fact, choices
|
|
||||||
convert this kind of variables into choice
|
|
||||||
"""
|
|
||||||
if variable.type in FORCE_CHOICE:
|
|
||||||
if not hasattr(self.objectspace.space, 'constraints'):
|
|
||||||
xmlfiles = variable.xmlfiles
|
|
||||||
self.objectspace.space.constraints = self.objectspace.constraints(xmlfiles)
|
|
||||||
self.objectspace.space.constraints.namespace = namespace
|
|
||||||
if not hasattr(self.objectspace.space.constraints, 'check'):
|
|
||||||
self.objectspace.space.constraints.check = []
|
|
||||||
check = self.objectspace.check(variable.xmlfiles)
|
|
||||||
check.name = 'valid_enum'
|
|
||||||
target = self.objectspace.target(variable.xmlfiles)
|
|
||||||
target.name = variable.path
|
|
||||||
check.target = [target]
|
|
||||||
check.namespace = namespace
|
|
||||||
check.param = []
|
|
||||||
for value in FORCE_CHOICE[variable.type]:
|
|
||||||
param = self.objectspace.param(variable.xmlfiles)
|
|
||||||
param.text = value
|
|
||||||
check.param.append(param)
|
|
||||||
self.objectspace.space.constraints.check.append(check)
|
|
||||||
variable.type = 'string'
|
|
||||||
|
|
||||||
def convert_test(self):
|
|
||||||
"""Convert variable tests value
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
if not hasattr(family, 'variable'):
|
|
||||||
continue
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.leadership):
|
|
||||||
for follower in variable.variable:
|
|
||||||
self._convert_test(follower)
|
|
||||||
else:
|
|
||||||
self._convert_test(variable)
|
|
||||||
|
|
||||||
def _convert_test(self,
|
|
||||||
variable,
|
|
||||||
) -> None:
|
|
||||||
if not hasattr(variable, 'information'):
|
|
||||||
variable.information = self.objectspace.information(variable.xmlfiles)
|
|
||||||
if hasattr(variable, 'test'):
|
|
||||||
if variable.test:
|
|
||||||
values = variable.test.split('|')
|
|
||||||
new_values = []
|
|
||||||
for value in values:
|
|
||||||
if value == '':
|
|
||||||
value = None
|
|
||||||
else:
|
|
||||||
value = CONVERT_OPTION.get(variable.type, {}).get('func', str)(value)
|
|
||||||
new_values.append(value)
|
|
||||||
variable.information.test = tuple(new_values)
|
|
||||||
del variable.test
|
|
||||||
|
|
||||||
def convert_help(self):
|
|
||||||
"""Convert variable help
|
|
||||||
"""
|
|
||||||
for families in self.objectspace.space.variables.values():
|
|
||||||
for family in families.family.values():
|
|
||||||
if not hasattr(family, 'variable'):
|
|
||||||
continue
|
|
||||||
for variable in family.variable.values():
|
|
||||||
if isinstance(variable, self.objectspace.leadership):
|
|
||||||
for follower in variable.variable:
|
|
||||||
self._convert_help(follower)
|
|
||||||
else:
|
|
||||||
self._convert_help(variable)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _convert_help(variable) -> None:
|
|
||||||
if hasattr(variable, 'help'):
|
|
||||||
variable.information.help = variable.help
|
|
||||||
del variable.help
|
|
@ -1,42 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Config file for Rougail
|
fichier de configuration pour rougail
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
"""
|
||||||
from os.path import join, abspath, dirname
|
from os.path import join, isfile, abspath, dirname
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
rougailroot = '/var/rougail'
|
||||||
|
patch_dir = join(rougailroot, 'patches')
|
||||||
|
manifests_dir = join(rougailroot, 'manifests')
|
||||||
|
templates_dir = join(rougailroot, 'templates')
|
||||||
|
dtddir = join(dirname(abspath(__file__)), 'data')
|
||||||
|
dtdfilename = join(dtddir, 'rougail.dtd')
|
||||||
|
|
||||||
ROUGAILROOT = '/var/rougail'
|
# chemin du répertoire source des fichiers templates
|
||||||
DTDDIR = join(dirname(abspath(__file__)), 'data')
|
patch_dir = '/srv/rougail/patch'
|
||||||
|
|
||||||
|
|
||||||
Config = {'rougailroot': ROUGAILROOT,
|
|
||||||
'patch_dir': join(ROUGAILROOT, 'patches'),
|
|
||||||
'manifests_dir': join(ROUGAILROOT, 'manifests'),
|
|
||||||
'templates_dir': join(ROUGAILROOT, 'templates'),
|
|
||||||
'dtdfilename': join(DTDDIR, 'rougail.dtd'),
|
|
||||||
'dtddir': DTDDIR,
|
|
||||||
'variable_namespace': 'rougail',
|
|
||||||
}
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
# Forked by:
|
# Forked by:
|
||||||
# Cadoles (http://www.cadoles.com)
|
# Cadoles (http://www.cadoles.com)
|
||||||
# Copyright (C) 2019-2021
|
# Copyright (C) 2019
|
||||||
|
|
||||||
# distribued with GPL-2 or later license
|
# distribued with GPL-2 or later license
|
||||||
|
|
||||||
@ -37,34 +37,68 @@
|
|||||||
<!-- root element -->
|
<!-- root element -->
|
||||||
<!-- =============== -->
|
<!-- =============== -->
|
||||||
|
|
||||||
<!ELEMENT rougail (services | variables | constraints | help)*>
|
<!ELEMENT rougail (services | family_action | variables | constraints | help)*>
|
||||||
|
|
||||||
<!-- ============== -->
|
<!-- ============== -->
|
||||||
<!-- files element -->
|
<!-- files element -->
|
||||||
<!-- ============== -->
|
<!-- ============== -->
|
||||||
|
|
||||||
|
<!ELEMENT family_action (action)>
|
||||||
|
<!ATTLIST family_action name CDATA #REQUIRED>
|
||||||
|
<!ATTLIST family_action description CDATA #IMPLIED>
|
||||||
|
<!ATTLIST family_action color CDATA #IMPLIED>
|
||||||
|
<!ATTLIST family_action image CDATA #IMPLIED>
|
||||||
|
<!ELEMENT action ((input* | profile* | ewtapp* | tag* | saltaction*)*)>
|
||||||
|
<!ATTLIST action type (form|custom|external|reader|apache) "custom">
|
||||||
|
<!ATTLIST action title CDATA #REQUIRED>
|
||||||
|
<!ATTLIST action description CDATA #REQUIRED>
|
||||||
|
<!ATTLIST action rewrite CDATA #IMPLIED>
|
||||||
|
<!ATTLIST action image CDATA #IMPLIED>
|
||||||
|
<!ATTLIST action actionlist CDATA #IMPLIED>
|
||||||
|
<!-- for apache action -->
|
||||||
|
<!ATTLIST action apache_path CDATA #IMPLIED>
|
||||||
|
<!ATTLIST action apache_path_type (FilenameOption|SymLinkOption|variable) "FilenameOption">
|
||||||
|
<!-- for external action -->
|
||||||
|
<!ATTLIST action url CDATA #IMPLIED>
|
||||||
|
<!ATTLIST action url_type (URLOption|SymLinkOption|variable) "URLOption">
|
||||||
|
<!-- for form action -->
|
||||||
|
<!ATTLIST action save (True|False) "False">
|
||||||
|
|
||||||
<!ELEMENT services (service*)>
|
<!ELEMENT services (service*)>
|
||||||
|
|
||||||
<!ELEMENT service ((port* | ip* | file* | override*)*) >
|
<!ELEMENT service ((port* | tcpwrapper* | ip* | interface* | package* | file* | digitalcertificate*)*) >
|
||||||
<!ATTLIST service name CDATA #REQUIRED>
|
<!ATTLIST service name CDATA #REQUIRED>
|
||||||
<!ATTLIST service manage (True|False) "True">
|
<!ATTLIST service method (systemd|upstart|apache|network) "systemd">
|
||||||
|
|
||||||
|
<!ELEMENT input (#PCDATA)>
|
||||||
|
<!ELEMENT profile (#PCDATA)>
|
||||||
|
<!ELEMENT ewtapp (#PCDATA)>
|
||||||
|
<!ELEMENT tag (#PCDATA)>
|
||||||
|
<!ELEMENT saltaction (#PCDATA)>
|
||||||
|
|
||||||
<!ELEMENT port (#PCDATA)>
|
<!ELEMENT port (#PCDATA)>
|
||||||
<!ATTLIST port port_type (PortOption|variable) "PortOption">
|
<!ATTLIST port port_type (PortOption|SymLinkOption|variable) "PortOption">
|
||||||
<!ATTLIST port portlist CDATA #IMPLIED >
|
<!ATTLIST port portlist CDATA #IMPLIED >
|
||||||
<!ATTLIST port protocol (tcp|udp) "tcp">
|
<!ATTLIST port protocol (tcp|udp) "tcp">
|
||||||
|
|
||||||
|
<!ELEMENT tcpwrapper (#PCDATA)>
|
||||||
|
<!ATTLIST tcpwrapper tcpwrapper_type (UnicodeOption|SymLinkOption|variable) "UnicodeOption">
|
||||||
|
<!ATTLIST tcpwrapper tcpwrapperlist CDATA #IMPLIED >
|
||||||
|
|
||||||
<!ELEMENT ip (#PCDATA)>
|
<!ELEMENT ip (#PCDATA)>
|
||||||
<!ATTLIST ip iplist CDATA #IMPLIED >
|
<!ATTLIST ip iplist CDATA #IMPLIED >
|
||||||
<!ATTLIST ip ip_type (NetworkOption|variable) "NetworkOption">
|
<!ATTLIST ip ip_type (NetworkOption|SymLinkOption|variable) "NetworkOption">
|
||||||
<!ATTLIST ip interface_type (UnicodeOption|variable) "UnicodeOption">
|
<!ATTLIST ip interface_type (UnicodeOption|SymLinkOption|variable) "UnicodeOption">
|
||||||
<!ATTLIST ip interface CDATA #REQUIRED>
|
<!ATTLIST ip interface CDATA #REQUIRED>
|
||||||
<!ATTLIST ip netmask_type (NetmaskOption|variable) "NetmaskOption">
|
<!ATTLIST ip netmask_type (NetmaskOption|SymLinkOption|variable) "NetmaskOption">
|
||||||
<!ATTLIST ip netmask CDATA "255.255.255.255">
|
<!ATTLIST ip netmask CDATA "255.255.255.255">
|
||||||
|
|
||||||
|
<!ELEMENT package (#PCDATA)>
|
||||||
|
<!ATTLIST package packagelist CDATA #IMPLIED >
|
||||||
|
|
||||||
<!ELEMENT file EMPTY>
|
<!ELEMENT file EMPTY>
|
||||||
<!ATTLIST file name CDATA #REQUIRED >
|
<!ATTLIST file name CDATA #REQUIRED >
|
||||||
<!ATTLIST file file_type (UnicodeOption|variable) "UnicodeOption">
|
<!ATTLIST file file_type (UnicodeOption|SymLinkOption|variable) "UnicodeOption">
|
||||||
<!ATTLIST file variable CDATA #IMPLIED>
|
<!ATTLIST file variable CDATA #IMPLIED>
|
||||||
<!ATTLIST file variable_type (variable) "variable">
|
<!ATTLIST file variable_type (variable) "variable">
|
||||||
<!ATTLIST file source CDATA #IMPLIED>
|
<!ATTLIST file source CDATA #IMPLIED>
|
||||||
@ -72,31 +106,35 @@
|
|||||||
<!ATTLIST file owner CDATA "root">
|
<!ATTLIST file owner CDATA "root">
|
||||||
<!ATTLIST file group CDATA "root">
|
<!ATTLIST file group CDATA "root">
|
||||||
<!ATTLIST file filelist CDATA #IMPLIED >
|
<!ATTLIST file filelist CDATA #IMPLIED >
|
||||||
|
<!ATTLIST file mkdir (True|False) "False">
|
||||||
|
<!ATTLIST file rm (True|False) "False">
|
||||||
<!ATTLIST file redefine (True|False) "False">
|
<!ATTLIST file redefine (True|False) "False">
|
||||||
<!ATTLIST file templating (True|False) "True">
|
|
||||||
|
|
||||||
<!ELEMENT override EMPTY>
|
<!ELEMENT digitalcertificate EMPTY>
|
||||||
<!ATTLIST override source CDATA #IMPLIED >
|
<!ATTLIST digitalcertificate name CDATA #REQUIRED >
|
||||||
<!ATTLIST override templating (True|False) "True">
|
<!ATTLIST digitalcertificate digitalcertificate_type (variable) "variable">
|
||||||
|
<!ATTLIST digitalcertificate certificate CDATA #REQUIRED >
|
||||||
|
<!ATTLIST digitalcertificate certificate_type (variable) "variable">
|
||||||
|
<!ATTLIST digitalcertificate type CDATA #REQUIRED >
|
||||||
|
<!ATTLIST digitalcertificate ca CDATA #REQUIRED >
|
||||||
|
|
||||||
<!ELEMENT variables (family*)>
|
<!ELEMENT variables (family*, separators*)>
|
||||||
|
<!ELEMENT family (#PCDATA | variable)*>
|
||||||
<!ELEMENT family (variable*)>
|
|
||||||
<!ATTLIST family name CDATA #REQUIRED>
|
<!ATTLIST family name CDATA #REQUIRED>
|
||||||
<!ATTLIST family description CDATA #IMPLIED>
|
<!ATTLIST family description CDATA #IMPLIED>
|
||||||
<!ATTLIST family help CDATA #IMPLIED>
|
|
||||||
<!ATTLIST family mode (basic|normal|expert) "basic">
|
<!ATTLIST family mode (basic|normal|expert) "basic">
|
||||||
|
<!ATTLIST family icon CDATA #IMPLIED>
|
||||||
<!ATTLIST family hidden (True|False) "False">
|
<!ATTLIST family hidden (True|False) "False">
|
||||||
<!ATTLIST family dynamic CDATA #IMPLIED>
|
<!ATTLIST family dynamic CDATA #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT variable (value*)>
|
<!ELEMENT variable (#PCDATA | value)*>
|
||||||
<!ATTLIST variable name CDATA #REQUIRED>
|
<!ATTLIST variable name CDATA #REQUIRED>
|
||||||
<!ATTLIST variable type (number|float|string|password|mail|boolean|filename|date|unix_user|ip|local_ip|netmask|network|broadcast|netbios|domain|hostname|web_address|port|mac|cidr|network_cidr|schedule|schedulemod) "string">
|
<!ATTLIST variable type CDATA #IMPLIED>
|
||||||
<!ATTLIST variable description CDATA #IMPLIED>
|
<!ATTLIST variable description CDATA #IMPLIED>
|
||||||
<!ATTLIST variable help CDATA #IMPLIED>
|
|
||||||
<!ATTLIST variable hidden (True|False) "False">
|
<!ATTLIST variable hidden (True|False) "False">
|
||||||
<!ATTLIST variable disabled (True|False) "False">
|
<!ATTLIST variable disabled (True|False) "False">
|
||||||
<!ATTLIST variable multi (True|False) "False">
|
<!ATTLIST variable multi (True|False) "False">
|
||||||
|
<!ATTLIST variable submulti (True|False) "False">
|
||||||
<!ATTLIST variable redefine (True|False) "False">
|
<!ATTLIST variable redefine (True|False) "False">
|
||||||
<!ATTLIST variable exists (True|False) "True">
|
<!ATTLIST variable exists (True|False) "True">
|
||||||
<!ATTLIST variable mandatory (True|False) "False">
|
<!ATTLIST variable mandatory (True|False) "False">
|
||||||
@ -105,40 +143,49 @@
|
|||||||
<!ATTLIST variable mode (basic|normal|expert) "normal">
|
<!ATTLIST variable mode (basic|normal|expert) "normal">
|
||||||
<!ATTLIST variable remove_check (True|False) "False">
|
<!ATTLIST variable remove_check (True|False) "False">
|
||||||
<!ATTLIST variable remove_condition (True|False) "False">
|
<!ATTLIST variable remove_condition (True|False) "False">
|
||||||
<!ATTLIST variable remove_fill (True|False) "False">
|
|
||||||
<!ATTLIST variable test CDATA #IMPLIED>
|
<!ATTLIST variable test CDATA #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT separators (separator*)>
|
||||||
|
|
||||||
|
<!ELEMENT separator (#PCDATA)>
|
||||||
|
<!ATTLIST separator name CDATA #REQUIRED>
|
||||||
|
<!ATTLIST separator never_hidden CDATA #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT value (#PCDATA)>
|
<!ELEMENT value (#PCDATA)>
|
||||||
|
|
||||||
<!ELEMENT constraints ((fill* | check* | condition* | group*)*)>
|
<!ELEMENT constraints ((fill* | check* | condition* | auto* | group*)*)>
|
||||||
|
<!ELEMENT fill (param*)>
|
||||||
<!ELEMENT fill ((target|param)+)>
|
|
||||||
<!ATTLIST fill name CDATA #REQUIRED>
|
<!ATTLIST fill name CDATA #REQUIRED>
|
||||||
|
<!ATTLIST fill target CDATA #REQUIRED>
|
||||||
|
|
||||||
<!ELEMENT check ((target|param)+)>
|
<!ELEMENT check (param*)>
|
||||||
<!ATTLIST check name CDATA #REQUIRED>
|
<!ATTLIST check name CDATA #REQUIRED>
|
||||||
|
<!ATTLIST check target CDATA #REQUIRED>
|
||||||
<!ATTLIST check level (error|warning) "error">
|
<!ATTLIST check level (error|warning) "error">
|
||||||
|
|
||||||
<!ELEMENT condition ((target|param)+)>
|
<!ELEMENT auto ((param)*)>
|
||||||
<!ATTLIST condition name (disabled_if_in|disabled_if_not_in|hidden_if_in|hidden_if_not_in|mandatory_if_in|mandatory_if_not_in) #REQUIRED>
|
<!ATTLIST auto name CDATA #REQUIRED>
|
||||||
|
<!ATTLIST auto target CDATA #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT condition ((target | param)+ )>
|
||||||
|
<!ATTLIST condition name CDATA #REQUIRED>
|
||||||
<!ATTLIST condition source CDATA #REQUIRED>
|
<!ATTLIST condition source CDATA #REQUIRED>
|
||||||
<!ATTLIST condition fallback (True|False) "False">
|
<!ATTLIST condition fallback (True|False) "False">
|
||||||
<!ATTLIST condition force_condition_on_fallback (True|False) "False">
|
|
||||||
<!ATTLIST condition force_inverse_condition_on_fallback (True|False) "False">
|
<!ELEMENT group (slave+)>
|
||||||
|
<!ATTLIST group master CDATA #REQUIRED>
|
||||||
|
<!ATTLIST group description CDATA #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT param (#PCDATA)>
|
<!ELEMENT param (#PCDATA)>
|
||||||
<!ATTLIST param type (string|number|nil|variable|information|suffix) "string">
|
<!ATTLIST param type (string|variable|number|python) "string">
|
||||||
<!ATTLIST param name CDATA #IMPLIED>
|
<!ATTLIST param name CDATA #IMPLIED>
|
||||||
<!ATTLIST param propertyerror (True|False) "True">
|
<!ATTLIST param hidden (True|False) "True">
|
||||||
<!ATTLIST param optional (True|False) "False">
|
<!ATTLIST param optional (True|False) "False">
|
||||||
|
|
||||||
<!ELEMENT target (#PCDATA)>
|
<!ELEMENT target (#PCDATA)>
|
||||||
<!ATTLIST target type (family|variable|filelist|iplist|portlist) "variable">
|
<!ATTLIST target type (family|variable|filelist|iplist|portlist|tcpwrapperlist|packagelist|actionlist) "variable">
|
||||||
<!ATTLIST target optional (True|False) "False">
|
<!ATTLIST target optional (True|False) "False">
|
||||||
|
|
||||||
<!ELEMENT group (follower+)>
|
<!ELEMENT slave (#PCDATA)>
|
||||||
<!ATTLIST group leader CDATA #REQUIRED>
|
|
||||||
<!ATTLIST group name CDATA #IMPLIED>
|
|
||||||
<!ATTLIST group description CDATA #IMPLIED>
|
|
||||||
|
|
||||||
<!ELEMENT follower (#PCDATA)>
|
<!ELEMENT help ((variable* | family*)*)>
|
||||||
|
@ -1,58 +1,27 @@
|
|||||||
"""Standard error classes
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Created by:
|
Erreurs Creole
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
"""
|
||||||
from .i18n import _
|
|
||||||
|
|
||||||
|
|
||||||
def display_xmlfiles(xmlfiles: list) -> str:
|
|
||||||
"""The function format xmlfiles informations to generate errors
|
|
||||||
"""
|
|
||||||
if len(xmlfiles) == 1:
|
|
||||||
return '"' + xmlfiles[0] + '"'
|
|
||||||
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"'
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(Exception):
|
class ConfigError(Exception):
|
||||||
"""Standard error for templating
|
pass
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class FileNotFound(ConfigError):
|
class FileNotFound(ConfigError):
|
||||||
"""Template file is not found
|
pass
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateError(ConfigError):
|
class TemplateError(ConfigError):
|
||||||
"""Templating generate an error
|
pass
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateDisabled(TemplateError):
|
class TemplateDisabled(TemplateError):
|
||||||
"""Template is disabled.
|
"""Template is disabled.
|
||||||
"""
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CreoleOperationError(Exception):
|
||||||
|
"""Type error or value Error for Creole variable's type or values
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class SpaceObjShallNotBeUpdated(Exception):
|
class SpaceObjShallNotBeUpdated(Exception):
|
||||||
@ -61,12 +30,7 @@ class SpaceObjShallNotBeUpdated(Exception):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class DictConsistencyError(Exception):
|
class CreoleDictConsistencyError(Exception):
|
||||||
"""It's not only that the Creole XML is valid against the Creole DTD
|
"""It's not only that the Creole XML is valid against the Creole DTD
|
||||||
it's that it is not consistent.
|
it's that it is not consistent.
|
||||||
"""
|
"""
|
||||||
def __init__(self, msg, errno, xmlfiles):
|
|
||||||
if xmlfiles:
|
|
||||||
msg = _(f'{msg} in {display_xmlfiles(xmlfiles)}')
|
|
||||||
super().__init__(msg)
|
|
||||||
self.errno = errno
|
|
||||||
|
@ -1,35 +1,30 @@
|
|||||||
"""Internationalisation utilities
|
# -*- coding: UTF-8 -*-
|
||||||
Created by:
|
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
|
||||||
EOLE (http://eole.orion.education.fr)
|
#
|
||||||
Copyright (C) 2005-2018
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by the
|
||||||
Forked by:
|
# Free Software Foundation, either version 3 of the License, or (at your
|
||||||
Cadoles (http://www.cadoles.com)
|
# option) any later version.
|
||||||
Copyright (C) 2019-2021
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
distribued with GPL-2 or later license
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||||
This program is free software; you can redistribute it and/or modify
|
# details.
|
||||||
it under the terms of the GNU General Public License as published by
|
#
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
(at your option) any later version.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
This program is distributed in the hope that it will be useful,
|
# The original `Config` design model is unproudly borrowed from
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# the whole pypy projet is under MIT licence
|
||||||
GNU General Public License for more details.
|
"internationalisation utilities"
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
import gettext
|
import gettext
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
# Application Name
|
# Application Name
|
||||||
APP_NAME = 'rougail'
|
APP_NAME = 'creole'
|
||||||
|
|
||||||
# Traduction dir
|
# Traduction dir
|
||||||
APP_DIR = os.path.join(sys.prefix, 'share')
|
APP_DIR = os.path.join(sys.prefix, 'share')
|
||||||
@ -49,8 +44,8 @@ mo_location = LOCALE_DIR
|
|||||||
|
|
||||||
gettext.find(APP_NAME, mo_location)
|
gettext.find(APP_NAME, mo_location)
|
||||||
gettext.textdomain(APP_NAME)
|
gettext.textdomain(APP_NAME)
|
||||||
#gettext.bind_textdomain_codeset(APP_NAME, "UTF-8")
|
gettext.bind_textdomain_codeset(APP_NAME, "UTF-8")
|
||||||
#gettext.translation(APP_NAME, fallback=True)
|
gettext.translation(APP_NAME, fallback=True)
|
||||||
|
|
||||||
t = gettext.translation(APP_NAME, fallback=True)
|
t = gettext.translation(APP_NAME, fallback=True)
|
||||||
|
|
||||||
|
594
src/rougail/loader.py
Normal file
594
src/rougail/loader.py
Normal file
@ -0,0 +1,594 @@
|
|||||||
|
"""creole loader
|
||||||
|
flattened XML specific
|
||||||
|
"""
|
||||||
|
from os.path import join, isfile, isdir
|
||||||
|
from os import listdir
|
||||||
|
#from ast import literal_eval
|
||||||
|
from lxml.etree import parse, DTD
|
||||||
|
|
||||||
|
from tiramisu import (StrOption, OptionDescription, DynOptionDescription, PortOption,
|
||||||
|
IntOption, ChoiceOption, BoolOption, SymLinkOption, IPOption,
|
||||||
|
NetworkOption, NetmaskOption, DomainnameOption, BroadcastOption,
|
||||||
|
URLOption, EmailOption, FilenameOption, UsernameOption, DateOption,
|
||||||
|
PasswordOption, BoolOption, MACOption, Leadership, submulti,
|
||||||
|
Params, ParamSelfOption, ParamOption, ParamValue, Calculation, calc_value,
|
||||||
|
groups, owners)
|
||||||
|
from tiramisu.error import ConfigError
|
||||||
|
|
||||||
|
from .config import dtdfilename
|
||||||
|
from .i18n import _
|
||||||
|
#For compatibility
|
||||||
|
from .xmlreflector import HIGH_COMPATIBILITY
|
||||||
|
#from . import eosfunc
|
||||||
|
from .objspace import CreoleObjSpace
|
||||||
|
from .utils import normalize_family
|
||||||
|
import imp
|
||||||
|
|
||||||
|
|
||||||
|
FUNC_TO_DICT = ['valid_not_equal']
|
||||||
|
|
||||||
|
|
||||||
|
class ConvertDynOptionDescription(DynOptionDescription):
|
||||||
|
def convert_suffix_to_path(self, suffix):
|
||||||
|
if not isinstance(suffix, str):
|
||||||
|
suffix = str(suffix)
|
||||||
|
return normalize_family(suffix,
|
||||||
|
check_name=False)
|
||||||
|
|
||||||
|
|
||||||
|
class CreoleLoaderError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def convert_tiramisu_value(value, obj):
|
||||||
|
"""
|
||||||
|
convertit les variables dans le bon type si nécessaire
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return value
|
||||||
|
def _convert_boolean(value):
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
prop = {'True': True,
|
||||||
|
'False': False,
|
||||||
|
'None': None}
|
||||||
|
if value not in prop:
|
||||||
|
raise Exception('unknown value {} while trying to cast {} to boolean'.format(value, obj))
|
||||||
|
return prop[value]
|
||||||
|
|
||||||
|
func = {IntOption: int, StrOption: str, PortOption: str,
|
||||||
|
DomainnameOption: str, EmailOption: str, URLOption: str,
|
||||||
|
IPOption: str, NetmaskOption: str, NetworkOption: str,
|
||||||
|
BroadcastOption: str, FilenameOption: str,
|
||||||
|
BoolOption: _convert_boolean}.get(obj, None)
|
||||||
|
if func is None:
|
||||||
|
return value
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [func(val) for val in value]
|
||||||
|
else:
|
||||||
|
return func(value)
|
||||||
|
|
||||||
|
|
||||||
|
CONVERT_OPTION = {'number': dict(opttype=IntOption),
|
||||||
|
'choice': dict(opttype=ChoiceOption),
|
||||||
|
'string': dict(opttype=StrOption),
|
||||||
|
'password': dict(opttype=PasswordOption),
|
||||||
|
'mail': dict(opttype=EmailOption),
|
||||||
|
'boolean': dict(opttype=BoolOption),
|
||||||
|
'symlink': dict(opttype=SymLinkOption),
|
||||||
|
'filename': dict(opttype=FilenameOption),
|
||||||
|
'date': dict(opttype=DateOption),
|
||||||
|
'unix_user': dict(opttype=UsernameOption),
|
||||||
|
'ip': dict(opttype=IPOption, initkwargs={'allow_reserved': True}),
|
||||||
|
'local_ip': dict(opttype=IPOption, initkwargs={'private_only': True, 'warnings_only': True}),
|
||||||
|
'netmask': dict(opttype=NetmaskOption),
|
||||||
|
'network': dict(opttype=NetworkOption),
|
||||||
|
'broadcast': dict(opttype=BroadcastOption),
|
||||||
|
'netbios': dict(opttype=DomainnameOption, initkwargs={'type': 'netbios', 'warnings_only': True}),
|
||||||
|
'domain': dict(opttype=DomainnameOption, initkwargs={'type': 'domainname', 'allow_ip': True, 'allow_without_dot': True}),
|
||||||
|
'domain_strict': dict(opttype=DomainnameOption, initkwargs={'type': 'domainname', 'allow_ip': False}),
|
||||||
|
'hostname': dict(opttype=DomainnameOption, initkwargs={'type': 'hostname', 'allow_ip': True}),
|
||||||
|
'hostname_strict': dict(opttype=DomainnameOption, initkwargs={'type': 'hostname', 'allow_ip': False}),
|
||||||
|
'web_address': dict(opttype=URLOption, initkwargs={'allow_ip': True, 'allow_without_dot': True}),
|
||||||
|
'port': dict(opttype=PortOption, initkwargs={'allow_private': True}),
|
||||||
|
'mac': dict(opttype=MACOption),
|
||||||
|
'cidr': dict(opttype=IPOption, initkwargs={'cidr': True}),
|
||||||
|
'network_cidr': dict(opttype=NetworkOption, initkwargs={'cidr': True}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Elt(object):
|
||||||
|
def __init__(self, attrib):
|
||||||
|
self.attrib = attrib
|
||||||
|
|
||||||
|
|
||||||
|
class PopulateTiramisuObjects(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.storage = ElementStorage()
|
||||||
|
self.booleans = []
|
||||||
|
self.force_store_values = set()
|
||||||
|
self.separators = {}
|
||||||
|
self.groups = {}
|
||||||
|
|
||||||
|
def parse_dtd(self, dtdfilename):
|
||||||
|
"""Loads the Creole DTD
|
||||||
|
|
||||||
|
:raises IOError: if the DTD is not found
|
||||||
|
|
||||||
|
:param dtdfilename: the full filename of the Creole DTD
|
||||||
|
"""
|
||||||
|
if not isfile(dtdfilename):
|
||||||
|
raise IOError(_("no such DTD file: {}").format(dtdfilename))
|
||||||
|
with open(dtdfilename, 'r') as dtdfd:
|
||||||
|
dtd = DTD(dtdfd)
|
||||||
|
for elt in dtd.iterelements():
|
||||||
|
if elt.name == 'variable':
|
||||||
|
for attr in elt.iterattributes():
|
||||||
|
if set(attr.itervalues()) == set(['True', 'False']):
|
||||||
|
self.booleans.append(attr.name)
|
||||||
|
|
||||||
|
def make_tiramisu_objects(self, xmlroot, creolefunc_file):
|
||||||
|
elt = Elt({'name': 'baseoption'})
|
||||||
|
if creolefunc_file is None:
|
||||||
|
self.eosfunc = None
|
||||||
|
else:
|
||||||
|
self.eosfunc = imp.load_source('eosfunc', creolefunc_file)
|
||||||
|
family = Family(elt, self.booleans, self.storage, self.eosfunc)
|
||||||
|
self.storage.add('.', family)
|
||||||
|
|
||||||
|
elts = {}
|
||||||
|
for elt in xmlroot:
|
||||||
|
elts.setdefault(elt.tag, []).append(elt)
|
||||||
|
list_elts = list(elts.keys())
|
||||||
|
if 'family' in list_elts:
|
||||||
|
list_elts.remove('family')
|
||||||
|
list_elts.insert(0, 'family')
|
||||||
|
for elt in list_elts:
|
||||||
|
xmlelts_ = elts[elt]
|
||||||
|
if elt == 'family':
|
||||||
|
xmlelts = []
|
||||||
|
actions = None
|
||||||
|
# `creole` family has to be loaded before any other family
|
||||||
|
# because `extra` family could use `creole` variables.
|
||||||
|
# `actions` family has to be loaded at the very end
|
||||||
|
# because it may use `creole` or `extra` variables
|
||||||
|
for xml in xmlelts_:
|
||||||
|
if xml.attrib['name'] == 'creole':
|
||||||
|
xmlelts.insert(0, xml)
|
||||||
|
elif xml.attrib['name'] == 'actions':
|
||||||
|
actions = xml
|
||||||
|
else:
|
||||||
|
xmlelts.append(xml)
|
||||||
|
if actions is not None:
|
||||||
|
xmlelts.append(actions)
|
||||||
|
else:
|
||||||
|
xmlelts = xmlelts_
|
||||||
|
for xmlelt in xmlelts:
|
||||||
|
if xmlelt.tag != 'family':
|
||||||
|
raise CreoleLoaderError(_('unknown tag {}').format(xmlelt.tag))
|
||||||
|
self._iter_family(xmlelt, family)
|
||||||
|
|
||||||
|
def _populate_variable(self, elt, subpath, is_follower, is_leader):
|
||||||
|
variable = Variable(elt, self.booleans, self.storage, is_follower, is_leader, self.eosfunc)
|
||||||
|
path = self._build_path(subpath, elt)
|
||||||
|
properties = variable.attrib.get('properties', [])
|
||||||
|
if 'force_store_value' in properties or "auto_freeze" in properties:
|
||||||
|
self.force_store_values.add(path)
|
||||||
|
self.storage.add(path, variable)
|
||||||
|
return variable
|
||||||
|
|
||||||
|
def _populate_family(self, elt, subpath):
|
||||||
|
if subpath is None:
|
||||||
|
force_icon = False
|
||||||
|
else:
|
||||||
|
force_icon = not subpath.startswith('containers') and not subpath.startswith('actions')
|
||||||
|
family = Family(elt, self.booleans, self.storage, self.eosfunc, force_icon)
|
||||||
|
path = self._build_path(subpath, elt)
|
||||||
|
self.storage.add(path, family)
|
||||||
|
return family
|
||||||
|
|
||||||
|
def _build_path(self, subpath, elt):
|
||||||
|
if subpath is None:
|
||||||
|
subpath = elt.attrib['name']
|
||||||
|
else:
|
||||||
|
subpath += '.' + elt.attrib['name']
|
||||||
|
return subpath
|
||||||
|
|
||||||
|
def _iter_leader(self, leader, subpath):
|
||||||
|
subpath = self._build_path(subpath, leader)
|
||||||
|
family = Family(leader, self.booleans, self.storage, self.eosfunc)
|
||||||
|
family.set_leader()
|
||||||
|
self.storage.add(subpath, family)
|
||||||
|
leader_name = None
|
||||||
|
for var in leader:
|
||||||
|
if var.tag == 'property':
|
||||||
|
self._parse_properties(family, var)
|
||||||
|
elif var.tag == 'variable':
|
||||||
|
if leader_name is None:
|
||||||
|
leader_name = var.attrib['name']
|
||||||
|
self.groups[leader_name] = []
|
||||||
|
else:
|
||||||
|
self.groups[leader_name].append(var.attrib['name'])
|
||||||
|
self._iter_family(var, family, subpath=subpath)
|
||||||
|
else:
|
||||||
|
raise CreoleLoaderError(_('unknown tag {}').format(var.tag))
|
||||||
|
return family
|
||||||
|
|
||||||
|
def _iter_family(self, child, family, subpath=None):
|
||||||
|
if child.tag not in ['family', 'variable', 'separators', 'leader', 'property']:
|
||||||
|
raise CreoleLoaderError(_('unknown tag {}').format(child.tag))
|
||||||
|
if child.tag == 'family':
|
||||||
|
old_family = family
|
||||||
|
family = self._populate_family(child, subpath)
|
||||||
|
if old_family is not None:
|
||||||
|
old_family.add(family)
|
||||||
|
if len(child) != 0:
|
||||||
|
subpath = self._build_path(subpath, child)
|
||||||
|
for c in child:
|
||||||
|
self._iter_family(c, family, subpath=subpath)
|
||||||
|
elif child.tag == 'leader':
|
||||||
|
leader = self._iter_leader(child, subpath)
|
||||||
|
family.add(leader)
|
||||||
|
elif child.tag == 'separators':
|
||||||
|
self._parse_separators(child)
|
||||||
|
elif child.tag == 'variable':
|
||||||
|
if family is None:
|
||||||
|
raise CreoleLoaderError(_('variable without family'))
|
||||||
|
|
||||||
|
is_follower = False
|
||||||
|
is_leader = False
|
||||||
|
if family.is_leader:
|
||||||
|
if child.attrib['name'] != family.attrib['name']:
|
||||||
|
is_follower = True
|
||||||
|
else:
|
||||||
|
is_leader = True
|
||||||
|
variable = self._populate_variable(child, subpath, is_follower, is_leader)
|
||||||
|
family.add(variable)
|
||||||
|
elif child.tag == 'property':
|
||||||
|
self._parse_properties(family, child)
|
||||||
|
else:
|
||||||
|
raise Exception('unknown tag {}'.format(child.tag))
|
||||||
|
|
||||||
|
def _parse_properties(self, family, child):
|
||||||
|
if child.get('type') == 'calculation':
|
||||||
|
kwargs = {'condition': child.attrib['source'],
|
||||||
|
'expected': ParamValue(child.attrib.get('expected'))}
|
||||||
|
if child.attrib['inverse'] == 'True':
|
||||||
|
kwargs['reverse_condition'] = ParamValue(True)
|
||||||
|
family.attrib['properties'].append((ParamValue(child.text), kwargs))
|
||||||
|
else:
|
||||||
|
family.attrib['properties'].append(child.text)
|
||||||
|
|
||||||
|
def _parse_separators(self, separators):
|
||||||
|
for separator in separators:
|
||||||
|
elt = self.storage.get(separator.attrib['name'])
|
||||||
|
never_hidden = separator.attrib.get('never_hidden')
|
||||||
|
if never_hidden == 'True':
|
||||||
|
never_hidden = True
|
||||||
|
else:
|
||||||
|
never_hidden = None
|
||||||
|
info = (separator.text, never_hidden)
|
||||||
|
self.separators[separator.attrib['name']] = info
|
||||||
|
elt.add_information('separator', info)
|
||||||
|
|
||||||
|
|
||||||
|
class ElementStorage:
|
||||||
|
def __init__(self):
|
||||||
|
self.paths = {}
|
||||||
|
|
||||||
|
def add(self, path, elt):
|
||||||
|
if path in self.paths:
|
||||||
|
raise CreoleLoaderError(_('path already loaded {}').format(path))
|
||||||
|
self.paths[path] = elt
|
||||||
|
|
||||||
|
def add_information(self, path, name, information):
|
||||||
|
elt = self.get(path)
|
||||||
|
elt.add_information(name, information)
|
||||||
|
|
||||||
|
def get(self, path):
|
||||||
|
if path not in self.paths:
|
||||||
|
raise CreoleLoaderError(_('there is no element for path {}').format(path))
|
||||||
|
return self.paths[path]
|
||||||
|
|
||||||
|
|
||||||
|
class Common:
|
||||||
|
def build_properties(self):
|
||||||
|
for index, prop in enumerate(self.attrib['properties']):
|
||||||
|
if isinstance(prop, tuple):
|
||||||
|
action, kwargs = prop
|
||||||
|
kwargs['condition'] = ParamOption(self.storage.get(kwargs['condition']).get(), todict=True)
|
||||||
|
prop = Calculation(calc_value,
|
||||||
|
Params(action,
|
||||||
|
kwargs=kwargs))
|
||||||
|
self.attrib['properties'][index] = prop
|
||||||
|
if self.attrib['properties']:
|
||||||
|
self.attrib['properties'] = tuple(self.attrib['properties'])
|
||||||
|
else:
|
||||||
|
del self.attrib['properties']
|
||||||
|
|
||||||
|
|
||||||
|
class Variable(Common):
|
||||||
|
def __init__(self, elt, booleans, storage, is_follower, is_leader, eosfunc):
|
||||||
|
self.option = None
|
||||||
|
self.informations = {}
|
||||||
|
self.attrib = {}
|
||||||
|
self.attrib['properties'] = []
|
||||||
|
self.attrib['validators'] = []
|
||||||
|
self.eosfunc = eosfunc
|
||||||
|
self.storage = storage
|
||||||
|
is_submulti = False
|
||||||
|
for key, value in elt.attrib.items():
|
||||||
|
if key in booleans:
|
||||||
|
if value == 'True':
|
||||||
|
value = True
|
||||||
|
elif value == 'False':
|
||||||
|
value = False
|
||||||
|
elif key == 'multi' and value == 'submulti':
|
||||||
|
is_submulti = True
|
||||||
|
value = submulti
|
||||||
|
else:
|
||||||
|
raise CreoleLoaderError(_('unknown value {} for {}').format(value, key))
|
||||||
|
if key in ['help', 'test']:
|
||||||
|
self.add_information(key, value)
|
||||||
|
elif key == 'type':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.attrib[key] = value
|
||||||
|
convert_option = CONVERT_OPTION[elt.attrib['type']]
|
||||||
|
self.object_type = convert_option['opttype']
|
||||||
|
if elt.attrib['type'] == 'choice':
|
||||||
|
if self.attrib.get('choice'):
|
||||||
|
self.attrib['values'] = getattr(self.eosfunc, self.attrib.get('choice'))
|
||||||
|
else:
|
||||||
|
self.attrib['values'] = []
|
||||||
|
for child in elt:
|
||||||
|
if child.tag == 'choice':
|
||||||
|
value = child.text
|
||||||
|
if 'type' in child.attrib and child.attrib['type'] == 'number':
|
||||||
|
value = int(value)
|
||||||
|
if value is None:
|
||||||
|
value = u''
|
||||||
|
self.attrib['values'].append(value)
|
||||||
|
self.attrib['values'] = tuple(self.attrib['values'])
|
||||||
|
for child in elt:
|
||||||
|
if child.tag == 'property':
|
||||||
|
if child.get('type') == 'calculation':
|
||||||
|
kwargs = {'condition': child.attrib['source'],
|
||||||
|
'expected': ParamValue(child.attrib.get('expected'))}
|
||||||
|
if child.attrib['inverse'] == 'True':
|
||||||
|
kwargs['reverse_condition'] = ParamValue(True)
|
||||||
|
self.attrib['properties'].append((ParamValue(child.text), kwargs))
|
||||||
|
else:
|
||||||
|
self.attrib['properties'].append(child.text)
|
||||||
|
elif child.tag == 'value':
|
||||||
|
if child.attrib.get('type') == 'calculation':
|
||||||
|
if child.text is not None and child.text.strip():
|
||||||
|
self.attrib['default'] = (child.text.strip(),)
|
||||||
|
else:
|
||||||
|
params = []
|
||||||
|
for param in child:
|
||||||
|
params.append(self.parse_param(param))
|
||||||
|
self.attrib['default'] = (child.attrib['name'], params, False)
|
||||||
|
else:
|
||||||
|
if "type" in child.attrib:
|
||||||
|
type_ = CONVERT_OPTION[child.attrib['type']]['opttype']
|
||||||
|
else:
|
||||||
|
type_ = self.object_type
|
||||||
|
if self.attrib['multi'] is True and not is_follower:
|
||||||
|
if 'default' not in self.attrib:
|
||||||
|
self.attrib['default'] = []
|
||||||
|
value = convert_tiramisu_value(child.text, type_)
|
||||||
|
self.attrib['default'].append(value)
|
||||||
|
if 'default_multi' not in self.attrib and not is_leader:
|
||||||
|
self.attrib['default_multi'] = value
|
||||||
|
elif self.attrib['multi'] == submulti:
|
||||||
|
if 'default' not in self.attrib:
|
||||||
|
self.attrib['default'] = []
|
||||||
|
value = convert_tiramisu_value(child.text, type_)
|
||||||
|
if not isinstance(value, list) and not is_follower:
|
||||||
|
value = [value]
|
||||||
|
self.attrib['default'].append(value)
|
||||||
|
if 'default_multi' not in self.attrib and not is_leader:
|
||||||
|
self.attrib['default_multi'] = value
|
||||||
|
else:
|
||||||
|
if 'default' in self.attrib:
|
||||||
|
raise CreoleLoaderError(_('default value already set for {}'
|
||||||
|
'').format(self.attrib['path']))
|
||||||
|
value = convert_tiramisu_value(child.text, type_)
|
||||||
|
if value is None: # and (elt.attrib['type'] != 'choice' or value not in self.attrib['values']):
|
||||||
|
value = u''
|
||||||
|
if is_follower:
|
||||||
|
self.attrib['default_multi'] = value
|
||||||
|
else:
|
||||||
|
self.attrib['default'] = value
|
||||||
|
elif child.tag == 'choice':
|
||||||
|
# already load
|
||||||
|
pass
|
||||||
|
elif child.tag == 'check':
|
||||||
|
params = []
|
||||||
|
for param in child:
|
||||||
|
params.append(self.parse_param(param))
|
||||||
|
#check.params = params
|
||||||
|
self.attrib['validators'].append((child.attrib['name'], params, child.attrib['warnings_only']))
|
||||||
|
else:
|
||||||
|
raise Exception('unknown tag {}'.format(child.tag))
|
||||||
|
if 'initkwargs' in convert_option:
|
||||||
|
self.attrib.update(convert_option['initkwargs'])
|
||||||
|
if elt.attrib['type'] == 'symlink':
|
||||||
|
del self.attrib['properties']
|
||||||
|
del self.attrib['multi']
|
||||||
|
self.attrib['opt'] = storage.get(self.attrib['opt'])
|
||||||
|
|
||||||
|
def parse_param(self, param):
|
||||||
|
name = param.attrib.get('name', '')
|
||||||
|
if param.attrib['type'] == 'string':
|
||||||
|
value = param.text
|
||||||
|
elif param.attrib['type'] == 'variable':
|
||||||
|
transitive = param.attrib.get('transitive', 'False')
|
||||||
|
if transitive == 'True':
|
||||||
|
transitive = True
|
||||||
|
elif transitive == 'False':
|
||||||
|
transitive = False
|
||||||
|
else:
|
||||||
|
raise CreoleLoaderError(_('unknown transitive boolean {}').format(transitive))
|
||||||
|
value = [param.text, transitive]
|
||||||
|
elif param.attrib['type'] == 'number':
|
||||||
|
value = int(param.text)
|
||||||
|
else:
|
||||||
|
raise CreoleLoaderError(_('unknown param type {}').format(param.attrib['type']))
|
||||||
|
return(name, value)
|
||||||
|
|
||||||
|
def add_information(self, key, value):
|
||||||
|
if key in self.informations:
|
||||||
|
raise CreoleLoaderError(_('key already exists in information {}').format(key))
|
||||||
|
self.informations[key] = value
|
||||||
|
|
||||||
|
def build_calculator(self, key):
|
||||||
|
if key in self.attrib:
|
||||||
|
values = self.attrib[key]
|
||||||
|
if isinstance(values, list):
|
||||||
|
is_list = True
|
||||||
|
else:
|
||||||
|
is_list = False
|
||||||
|
values = [values]
|
||||||
|
ret = []
|
||||||
|
for value in values:
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
if key == 'validators':
|
||||||
|
args = [ParamSelfOption()]
|
||||||
|
else:
|
||||||
|
args = []
|
||||||
|
kwargs = {}
|
||||||
|
if len(value) == 3:
|
||||||
|
for param in value[1]:
|
||||||
|
if isinstance(param[1], list):
|
||||||
|
if value[0] in FUNC_TO_DICT:
|
||||||
|
param_value = ParamOption(self.storage.get(param[1][0]).get(), notraisepropertyerror=param[1][1], todict=True)
|
||||||
|
else:
|
||||||
|
param_value = ParamOption(self.storage.get(param[1][0]).get(), notraisepropertyerror=param[1][1])
|
||||||
|
else:
|
||||||
|
param_value = ParamValue(param[1])
|
||||||
|
if not param[0]:
|
||||||
|
args.append(param_value)
|
||||||
|
else:
|
||||||
|
kwargs[param[0]] = param_value
|
||||||
|
|
||||||
|
ret.append(Calculation(getattr(self.eosfunc, value[0]),
|
||||||
|
Params(tuple(args),
|
||||||
|
kwargs=kwargs)))
|
||||||
|
else:
|
||||||
|
ret.append(value)
|
||||||
|
if not is_list:
|
||||||
|
self.attrib[key] = ret[0]
|
||||||
|
else:
|
||||||
|
self.attrib[key] = ret
|
||||||
|
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
if self.option is None:
|
||||||
|
if self.object_type is SymLinkOption:
|
||||||
|
self.attrib['opt'] = self.attrib['opt'].get()
|
||||||
|
else:
|
||||||
|
self.build_properties()
|
||||||
|
self.build_calculator('default')
|
||||||
|
self.build_calculator('validators')
|
||||||
|
if not self.attrib['validators']:
|
||||||
|
del self.attrib['validators']
|
||||||
|
try:
|
||||||
|
option = self.object_type(**self.attrib)
|
||||||
|
except Exception as err:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
name = self.attrib['name']
|
||||||
|
raise CreoleLoaderError(_('cannot create option {}: {}').format(name, err))
|
||||||
|
for key, value in self.informations.items():
|
||||||
|
option.impl_set_information(key, value)
|
||||||
|
self.option = option
|
||||||
|
return self.option
|
||||||
|
|
||||||
|
|
||||||
|
class Family(Common):
|
||||||
|
def __init__(self, elt, booleans, storage, eosfunc, force_icon=False):
|
||||||
|
self.option = None
|
||||||
|
self.attrib = {}
|
||||||
|
self.is_leader = False
|
||||||
|
if force_icon:
|
||||||
|
self.informations = {'icon': None}
|
||||||
|
else:
|
||||||
|
self.informations = {}
|
||||||
|
self.children = []
|
||||||
|
self.storage = storage
|
||||||
|
self.eosfunc = eosfunc
|
||||||
|
self.attrib['properties'] = []
|
||||||
|
for key, value in elt.attrib.items():
|
||||||
|
if key in booleans:
|
||||||
|
if value == 'True':
|
||||||
|
value = True
|
||||||
|
elif value == 'False':
|
||||||
|
value = False
|
||||||
|
else:
|
||||||
|
raise CreoleLoaderError(_('unknown value {} for {}').format(value, key))
|
||||||
|
if key == 'icon':
|
||||||
|
self.add_information('icon', value)
|
||||||
|
continue
|
||||||
|
elif key == 'hidden':
|
||||||
|
if value:
|
||||||
|
self.attrib['properties'].append(key)
|
||||||
|
elif key == 'mode':
|
||||||
|
self.attrib['properties'].append(value)
|
||||||
|
elif key == 'help':
|
||||||
|
self.add_information(key, value)
|
||||||
|
elif key == 'type':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.attrib[key] = value
|
||||||
|
if 'doc' not in self.attrib:
|
||||||
|
self.attrib['doc'] = u''
|
||||||
|
|
||||||
|
def add(self, child):
|
||||||
|
self.children.append(child)
|
||||||
|
|
||||||
|
def add_information(self, key, value):
|
||||||
|
if key in self.informations and not (key == 'icon' and self.informations[key] is None):
|
||||||
|
raise CreoleLoaderError(_('key already exists in information {}').format(key))
|
||||||
|
self.informations[key] = value
|
||||||
|
|
||||||
|
def set_leader(self):
|
||||||
|
self.is_leader = True
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
if self.option is None:
|
||||||
|
self.attrib['children'] = []
|
||||||
|
for child in self.children:
|
||||||
|
self.attrib['children'].append(child.get())
|
||||||
|
self.build_properties()
|
||||||
|
try:
|
||||||
|
if 'dynamic' in self.attrib:
|
||||||
|
dynamic = self.storage.get(self.attrib['dynamic']).get()
|
||||||
|
del self.attrib['dynamic']
|
||||||
|
self.attrib['suffixes'] = Calculation(self.eosfunc.calc_value,
|
||||||
|
Params((ParamOption(dynamic),)))
|
||||||
|
option = ConvertDynOptionDescription(**self.attrib)
|
||||||
|
elif not self.is_leader:
|
||||||
|
option = OptionDescription(**self.attrib)
|
||||||
|
else:
|
||||||
|
option = Leadership(**self.attrib)
|
||||||
|
except Exception as err:
|
||||||
|
raise CreoleLoaderError(_('cannot create optiondescription {}: {}').format(self.attrib['name'], err))
|
||||||
|
for key, value in self.informations.items():
|
||||||
|
option.impl_set_information(key, value)
|
||||||
|
self.option = option
|
||||||
|
#if self.is_leader:
|
||||||
|
# self.option.impl_set_group_type(groups.leader)
|
||||||
|
|
||||||
|
return self.option
|
||||||
|
|
||||||
|
|
||||||
|
def load(xmlroot: str,
|
||||||
|
dtd_path: str,
|
||||||
|
funcs_path: str):
|
||||||
|
tiramisu_objects = PopulateTiramisuObjects()
|
||||||
|
tiramisu_objects.parse_dtd(dtd_path)
|
||||||
|
tiramisu_objects.make_tiramisu_objects(xmlroot,
|
||||||
|
funcs_path)
|
||||||
|
return tiramisu_objects.storage.paths['.'].get()
|
@ -1,494 +1,696 @@
|
|||||||
"""parse XML files and build a space with objects
|
|
||||||
it aggregates this files and manage redefine and exists attributes
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
"""
|
||||||
from typing import Optional
|
Creole flattener. Takes a bunch of Creole XML dispatched in differents folders
|
||||||
|
as an input and outputs a human readable flatened XML
|
||||||
|
|
||||||
|
Sample usage::
|
||||||
|
|
||||||
|
>>> from creole.objspace import CreoleObjSpace
|
||||||
|
>>> eolobj = CreoleObjSpace('/usr/share/creole/creole.dtd')
|
||||||
|
>>> eolobj.create_or_populate_from_xml('creole', ['/usr/share/eole/creole/dicos'])
|
||||||
|
>>> eolobj.space_visitor()
|
||||||
|
>>> eolobj.save('/tmp/creole_flatened_output.xml')
|
||||||
|
|
||||||
|
The CreoleObjSpace
|
||||||
|
|
||||||
|
- loads the XML into an internal CreoleObjSpace representation
|
||||||
|
- visits/annotates the objects
|
||||||
|
- dumps the object space as XML output into a single XML target
|
||||||
|
|
||||||
|
The visit/annotation stage is a complex step that corresponds to the Creole
|
||||||
|
procedures.
|
||||||
|
|
||||||
|
For example: a variable is redefined and shall be moved to another family
|
||||||
|
means that a variable1 = Variable() object in the object space who lives in the family1 parent
|
||||||
|
has to be moved in family2. The visit procedure changes the varable1's object space's parent.
|
||||||
|
"""
|
||||||
|
from collections import OrderedDict
|
||||||
|
from lxml.etree import Element, SubElement # pylint: disable=E0611
|
||||||
|
from json import dump
|
||||||
|
|
||||||
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .xmlreflector import XMLReflector
|
from .xmlreflector import XMLReflector, HIGH_COMPATIBILITY
|
||||||
|
from .annotator import ERASED_ATTRIBUTES, ActionAnnotator, ServiceAnnotator, SpaceAnnotator
|
||||||
from .utils import normalize_family
|
from .utils import normalize_family
|
||||||
from .error import SpaceObjShallNotBeUpdated, DictConsistencyError
|
from .error import CreoleOperationError, SpaceObjShallNotBeUpdated, CreoleDictConsistencyError
|
||||||
from .path import Path
|
|
||||||
from .config import Config
|
|
||||||
|
|
||||||
# RougailObjSpace's elements that shall be forced to the Redefinable type
|
# CreoleObjSpace's elements like 'family' or 'slave', that shall be forced to the Redefinable type
|
||||||
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
|
FORCE_REDEFINABLES = ('family', 'slave', 'service', 'disknod', 'variables', 'family_action')
|
||||||
# RougailObjSpace's elements that shall be forced to the UnRedefinable type
|
# CreoleObjSpace's elements that shall be forced to the UnRedefinable type
|
||||||
FORCE_UNREDEFINABLES = ('value',)
|
FORCE_UNREDEFINABLES = ('value', 'input', 'profile', 'ewtapp', 'tag', 'saltaction')
|
||||||
# RougailObjSpace's elements that shall not be modify
|
# CreoleObjSpace's elements that shall be set to the UnRedefinable type
|
||||||
UNREDEFINABLE = ('multi', 'type')
|
UNREDEFINABLE = ('submulti', 'multi', 'type')
|
||||||
# RougailObjSpace's elements that did not created automaticly
|
|
||||||
FORCE_ELEMENTS = ('choice', 'property_', 'leadership', 'information')
|
|
||||||
# XML text are convert has name
|
|
||||||
FORCED_TEXT_ELTS_AS_NAME = ('choice', 'property', 'value', 'target')
|
|
||||||
|
|
||||||
|
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
|
||||||
|
'force_store_value', 'disabled', 'mandatory')
|
||||||
|
CONVERT_PROPERTIES = {'auto_save': ['force_store_value'], 'auto_freeze': ['force_store_value', 'auto_freeze']}
|
||||||
|
|
||||||
|
RENAME_ATTIBUTES = {'description': 'doc'}
|
||||||
|
|
||||||
|
INCOMPATIBLE_ATTRIBUTES = [['multi', 'submulti']]
|
||||||
|
|
||||||
|
#TYPE_TARGET_CONDITION = ('variable', 'family')
|
||||||
|
|
||||||
# _____________________________________________________________________________
|
# _____________________________________________________________________________
|
||||||
# special types definitions for the Object Space's internal representation
|
# special types definitions for the Object Space's internal representation
|
||||||
class RootRougailObject: # pylint: disable=R0903
|
class RootCreoleObject(object):
|
||||||
"""Root object
|
""
|
||||||
|
|
||||||
|
|
||||||
|
class CreoleObjSpace(object):
|
||||||
|
"""DOM XML reflexion free internal representation of a Creole Dictionary
|
||||||
"""
|
"""
|
||||||
def __init__(self,
|
choice = type('Choice', (RootCreoleObject,), OrderedDict())
|
||||||
xmlfiles,
|
property_ = type('Property', (RootCreoleObject,), OrderedDict())
|
||||||
name=None,
|
# Creole ObjectSpace's Leadership variable class type
|
||||||
):
|
Leadership = type('Leadership', (RootCreoleObject,), OrderedDict())
|
||||||
if not isinstance(xmlfiles, list):
|
|
||||||
xmlfiles = [xmlfiles]
|
|
||||||
self.xmlfiles = xmlfiles
|
|
||||||
if name:
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
|
|
||||||
class Atom(RootRougailObject): # pylint: disable=R0903
|
|
||||||
"""Atomic object (means can only define one time)
|
|
||||||
"""
|
"""
|
||||||
|
This Atom type stands for singleton, that is
|
||||||
|
an Object Space's atom object is present only once in the
|
||||||
class Redefinable(RootRougailObject): # pylint: disable=R0903
|
object space's tree
|
||||||
"""Object that could be redefine
|
|
||||||
"""
|
"""
|
||||||
|
Atom = type('Atom', (RootCreoleObject,), OrderedDict())
|
||||||
|
"A variable that can't be redefined"
|
||||||
|
Redefinable = type('Redefinable', (RootCreoleObject,), OrderedDict())
|
||||||
|
"A variable can be redefined"
|
||||||
|
UnRedefinable = type('UnRedefinable', (RootCreoleObject,), OrderedDict())
|
||||||
|
|
||||||
|
|
||||||
class UnRedefinable(RootRougailObject): # pylint: disable=R0903
|
def __init__(self, dtdfilename): # pylint: disable=R0912
|
||||||
"""Object that could not be redefine
|
self.index = 0
|
||||||
"""
|
class ObjSpace(object): # pylint: disable=R0903
|
||||||
|
"""
|
||||||
|
Base object space
|
||||||
class ObjSpace: # pylint: disable=R0903
|
"""
|
||||||
"""
|
|
||||||
Base object space
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class RougailObjSpace:
|
|
||||||
"""Rougail ObjectSpace is an object's reflexion of the XML elements
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
xmlreflector: XMLReflector,
|
|
||||||
) -> None:
|
|
||||||
self.space = ObjSpace()
|
self.space = ObjSpace()
|
||||||
self.paths = Path()
|
self.xmlreflector = XMLReflector()
|
||||||
|
self.xmlreflector.parse_dtd(dtdfilename)
|
||||||
|
self.redefine_variables = None
|
||||||
|
self.probe_variables = []
|
||||||
|
|
||||||
self.forced_text_elts_as_name = set(FORCED_TEXT_ELTS_AS_NAME)
|
# ['variable', 'separator', 'family']
|
||||||
|
self.forced_text_elts = set()
|
||||||
|
self.forced_text_elts_as_name = set(['choice', 'property'])
|
||||||
|
self.forced_choice_option = {}
|
||||||
|
self.paths = Path()
|
||||||
self.list_conditions = {}
|
self.list_conditions = {}
|
||||||
self.valid_enums = {}
|
|
||||||
self.booleans_attributs = []
|
self.booleans_attributs = []
|
||||||
|
|
||||||
self.make_object_space_classes(xmlreflector)
|
for elt in self.xmlreflector.dtd.iterelements():
|
||||||
|
|
||||||
def make_object_space_classes(self,
|
|
||||||
xmlreflector: XMLReflector,
|
|
||||||
) -> None:
|
|
||||||
"""Create Rougail ObjectSpace class types from DDT file
|
|
||||||
It enables us to create objects like:
|
|
||||||
File(), Variable(), Ip(), Family(), Constraints()... and so on.
|
|
||||||
"""
|
|
||||||
|
|
||||||
for dtd_elt in xmlreflector.dtd.iterelements():
|
|
||||||
attrs = {}
|
attrs = {}
|
||||||
if dtd_elt.name in FORCE_REDEFINABLES:
|
clstype = self.UnRedefinable
|
||||||
clstype = Redefinable
|
atomic = True
|
||||||
elif not dtd_elt.attributes() and dtd_elt.name not in FORCE_UNREDEFINABLES:
|
forced_text_elt = False
|
||||||
clstype = Atom
|
if elt.type == 'mixed':
|
||||||
else:
|
forced_text_elt = True
|
||||||
clstype = UnRedefinable
|
if elt.name == 'service':
|
||||||
forced_text_elt = dtd_elt.type == 'mixed'
|
self.parse_dtd_right_left_elt(elt.content)
|
||||||
for dtd_attr in dtd_elt.iterattributes():
|
for attr in elt.iterattributes():
|
||||||
if set(dtd_attr.itervalues()) == {'True', 'False'}:
|
atomic = False
|
||||||
# it's a boolean
|
if attr.default_value:
|
||||||
self.booleans_attributs.append(dtd_attr.name)
|
if attr.default_value == 'True':
|
||||||
if dtd_attr.default_value:
|
default_value = True
|
||||||
# set default value for this attribute
|
elif attr.default_value == 'False':
|
||||||
default_value = dtd_attr.default_value
|
default_value = False
|
||||||
if dtd_attr.name in self.booleans_attributs:
|
else:
|
||||||
default_value = self.convert_boolean(default_value)
|
default_value = attr.default_value
|
||||||
attrs[dtd_attr.name] = default_value
|
attrs[attr.name] = default_value
|
||||||
if dtd_attr.name == 'redefine':
|
if not attr.name.endswith('_type'):
|
||||||
# has a redefine attribute, so it's a Redefinable object
|
values = list(attr.itervalues())
|
||||||
clstype = Redefinable
|
if values != []:
|
||||||
if dtd_attr.name == 'name' and forced_text_elt:
|
self.forced_choice_option.setdefault(elt.name, {})[attr.name] = values
|
||||||
# child.text should be transform has a "name" attribute
|
|
||||||
|
if attr.name == 'redefine':
|
||||||
|
clstype = self.Redefinable
|
||||||
|
if attr.name == 'name' and forced_text_elt is True:
|
||||||
|
self.forced_text_elts.add(elt.name)
|
||||||
forced_text_elt = False
|
forced_text_elt = False
|
||||||
|
|
||||||
|
if set(attr.itervalues()) == set(['True', 'False']):
|
||||||
|
self.booleans_attributs.append(attr.name)
|
||||||
|
|
||||||
if forced_text_elt is True:
|
if forced_text_elt is True:
|
||||||
self.forced_text_elts_as_name.add(dtd_elt.name)
|
self.forced_text_elts_as_name.add(elt.name)
|
||||||
|
|
||||||
# create ObjectSpace object
|
if elt.name in FORCE_REDEFINABLES:
|
||||||
setattr(self, dtd_elt.name, type(dtd_elt.name.capitalize(), (clstype,), attrs))
|
clstype = self.Redefinable
|
||||||
for elt in FORCE_ELEMENTS:
|
elif elt.name in FORCE_UNREDEFINABLES:
|
||||||
setattr(self, elt, type(self._get_elt_name(elt), (RootRougailObject,), dict()))
|
clstype = self.UnRedefinable
|
||||||
|
elif atomic:
|
||||||
|
clstype = self.Atom
|
||||||
|
|
||||||
@staticmethod
|
# Creole ObjectSpace class types, it enables us to create objects like:
|
||||||
def _get_elt_name(elt) -> str:
|
# Service_restriction(), Ip(), Interface(), Host(), Fstab(), Package(), Disknod(),
|
||||||
name = elt.capitalize()
|
# File(), Variables(), Family(), Variable(), Separators(), Separator(), Value(),
|
||||||
if name.endswith('_'):
|
# Constraints()... and so on. Creole ObjectSpace is an object's reflexion of
|
||||||
name = name[:-1]
|
# the XML elements
|
||||||
return name
|
setattr(self, elt.name, type(elt.name.capitalize(), (clstype,), attrs))
|
||||||
|
|
||||||
def xml_parse_document(self,
|
def parse_dtd_right_left_elt(self, elt):
|
||||||
xmlfile,
|
if elt.right.type == 'or':
|
||||||
document,
|
self.parse_dtd_right_left_elt(elt.right)
|
||||||
space,
|
|
||||||
namespace,
|
|
||||||
):
|
|
||||||
"""Parses a Rougail XML file and populates the RougailObjSpace
|
|
||||||
"""
|
|
||||||
if namespace in ['services', Config['variable_namespace']]:
|
|
||||||
msg = _(f'Namespace name "{namespace}" is not allowed')
|
|
||||||
raise DictConsistencyError(msg, 21, xmlfile)
|
|
||||||
if not namespace:
|
|
||||||
namespace = Config['variable_namespace']
|
|
||||||
redefine_variables = []
|
|
||||||
self._xml_parse(xmlfile,
|
|
||||||
document,
|
|
||||||
space,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _xml_parse(self, # pylint: disable=R0913
|
def _convert_boolean(self, value): # pylint: disable=R0201
|
||||||
xmlfile,
|
"""Boolean coercion. The Creole XML may contain srings like `True` or `False`
|
||||||
document,
|
|
||||||
space,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
) -> None:
|
|
||||||
# var to check unique family name in a XML file
|
|
||||||
family_names = []
|
|
||||||
for child in document:
|
|
||||||
if not isinstance(child.tag, str):
|
|
||||||
# doesn't proceed the XML commentaries
|
|
||||||
continue
|
|
||||||
if child.tag == 'family':
|
|
||||||
if child.attrib['name'] in family_names:
|
|
||||||
msg = _(f'Family "{child.attrib["name"]}" is set several times')
|
|
||||||
raise DictConsistencyError(msg, 44, xmlfile)
|
|
||||||
family_names.append(child.attrib['name'])
|
|
||||||
try:
|
|
||||||
# variable objects creation
|
|
||||||
variableobj = self.get_variableobj(xmlfile,
|
|
||||||
child,
|
|
||||||
space,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
)
|
|
||||||
except SpaceObjShallNotBeUpdated:
|
|
||||||
continue
|
|
||||||
self.set_text(child,
|
|
||||||
variableobj,
|
|
||||||
)
|
|
||||||
self.set_attributes(xmlfile,
|
|
||||||
child,
|
|
||||||
variableobj,
|
|
||||||
)
|
|
||||||
self.remove(child,
|
|
||||||
variableobj,
|
|
||||||
redefine_variables,
|
|
||||||
)
|
|
||||||
self.set_path(namespace,
|
|
||||||
document,
|
|
||||||
variableobj,
|
|
||||||
)
|
|
||||||
self.add_to_tree_structure(variableobj,
|
|
||||||
space,
|
|
||||||
child,
|
|
||||||
namespace,
|
|
||||||
)
|
|
||||||
if list(child) != []:
|
|
||||||
self._xml_parse(xmlfile,
|
|
||||||
child,
|
|
||||||
variableobj,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_variableobj(self,
|
|
||||||
xmlfile: str,
|
|
||||||
child: list,
|
|
||||||
space,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
): # pylint: disable=R0913
|
|
||||||
"""
|
|
||||||
retrieves or creates Rougail Object Subspace objects
|
|
||||||
"""
|
|
||||||
obj = getattr(self, child.tag)
|
|
||||||
name = self._get_name(child, namespace)
|
|
||||||
if Redefinable in obj.__mro__:
|
|
||||||
return self.create_or_update_redefinable_object(xmlfile,
|
|
||||||
child.attrib,
|
|
||||||
space,
|
|
||||||
child,
|
|
||||||
name,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
)
|
|
||||||
if Atom in obj.__mro__:
|
|
||||||
if child.tag in vars(space):
|
|
||||||
# Atom instance has to be a singleton here
|
|
||||||
# we do not re-create it, we reuse it
|
|
||||||
return getattr(space, child.tag)
|
|
||||||
return obj(xmlfile, name)
|
|
||||||
if child.tag not in vars(space):
|
|
||||||
setattr(space, child.tag, [])
|
|
||||||
return obj(xmlfile, name)
|
|
||||||
|
|
||||||
def _get_name(self,
|
|
||||||
child,
|
|
||||||
namespace: str,
|
|
||||||
) -> Optional[str]:
|
|
||||||
if child.tag == 'variables':
|
|
||||||
return namespace
|
|
||||||
if 'name' in child.attrib:
|
|
||||||
return child.attrib['name']
|
|
||||||
if child.text and child.tag in self.forced_text_elts_as_name:
|
|
||||||
return child.text.strip()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def create_or_update_redefinable_object(self,
|
|
||||||
xmlfile,
|
|
||||||
subspace,
|
|
||||||
space,
|
|
||||||
child,
|
|
||||||
name,
|
|
||||||
namespace,
|
|
||||||
redefine_variables,
|
|
||||||
): # pylint: disable=R0913
|
|
||||||
"""A redefinable object could be created or updated
|
|
||||||
"""
|
|
||||||
existed_var = self.get_existed_obj(name,
|
|
||||||
space,
|
|
||||||
child,
|
|
||||||
namespace,
|
|
||||||
)
|
|
||||||
if existed_var:
|
|
||||||
# if redefine is set to object, default value is False
|
|
||||||
# otherwise it's always a redefinable object
|
|
||||||
default_redefine = child.tag in FORCE_REDEFINABLES
|
|
||||||
redefine = self.convert_boolean(subspace.get('redefine', default_redefine))
|
|
||||||
if redefine is True:
|
|
||||||
if isinstance(existed_var, self.variable): # pylint: disable=E1101
|
|
||||||
if namespace == Config['variable_namespace']:
|
|
||||||
redefine_variables.append(name)
|
|
||||||
else:
|
|
||||||
redefine_variables.append(space.path + '.' + name)
|
|
||||||
existed_var.xmlfiles.append(xmlfile)
|
|
||||||
return existed_var
|
|
||||||
exists = self.convert_boolean(subspace.get('exists', True))
|
|
||||||
if exists is False:
|
|
||||||
raise SpaceObjShallNotBeUpdated()
|
|
||||||
msg = _(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", '
|
|
||||||
f'already defined')
|
|
||||||
raise DictConsistencyError(msg, 45, existed_var.xmlfiles)
|
|
||||||
# object deos not exists
|
|
||||||
exists = self.convert_boolean(subspace.get('exists', False))
|
|
||||||
if exists is True:
|
|
||||||
# manage object only if already exists, so cancel
|
|
||||||
raise SpaceObjShallNotBeUpdated()
|
|
||||||
redefine = self.convert_boolean(subspace.get('redefine', False))
|
|
||||||
if redefine is True:
|
|
||||||
# cannot redefine an inexistant object
|
|
||||||
msg = _(f'Redefined object: "{name}" does not exist yet')
|
|
||||||
raise DictConsistencyError(msg, 46, xmlfile)
|
|
||||||
if child.tag not in vars(space):
|
|
||||||
setattr(space, child.tag, {})
|
|
||||||
return getattr(self, child.tag)(xmlfile, name)
|
|
||||||
|
|
||||||
def get_existed_obj(self,
|
|
||||||
name: str,
|
|
||||||
space: str,
|
|
||||||
child,
|
|
||||||
namespace: str,
|
|
||||||
) -> None:
|
|
||||||
"""if an object exists, return it
|
|
||||||
"""
|
|
||||||
if child.tag in ['variable', 'family']:
|
|
||||||
name = normalize_family(name)
|
|
||||||
if isinstance(space, self.family): # pylint: disable=E1101
|
|
||||||
if namespace != Config['variable_namespace']:
|
|
||||||
name = space.path + '.' + name
|
|
||||||
if not self.paths.path_is_defined(name):
|
|
||||||
return None
|
|
||||||
old_family_name = self.paths.get_variable_family_path(name)
|
|
||||||
if space.path != old_family_name:
|
|
||||||
msg = _(f'Variable was previously create in family "{old_family_name}", '
|
|
||||||
f'now it is in "{space.path}"')
|
|
||||||
raise DictConsistencyError(msg, 47, space.xmlfiles)
|
|
||||||
return self.paths.get_variable(name)
|
|
||||||
# it's not a family
|
|
||||||
children = getattr(space, child.tag, {})
|
|
||||||
if name in children:
|
|
||||||
return children[name]
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def convert_boolean(value: str) -> bool:
|
|
||||||
"""Boolean coercion. The Rougail XML may contain srings like `True` or `False`
|
|
||||||
"""
|
"""
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return value
|
return value
|
||||||
if value == 'True':
|
if value == 'True':
|
||||||
return True
|
return True
|
||||||
return False
|
elif value == 'False':
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise TypeError(_('{} is not True or False').format(value)) # pragma: no cover
|
||||||
|
|
||||||
def set_text(self,
|
def _is_already_exists(self, name, space, child, namespace):
|
||||||
child,
|
if isinstance(space, self.family): # pylint: disable=E1101
|
||||||
variableobj,
|
if namespace != 'creole':
|
||||||
) -> None:
|
name = space.path + '.' + name
|
||||||
"""set text
|
return self.paths.path_is_defined(name)
|
||||||
|
if child.tag in ['family', 'family_action']:
|
||||||
|
norm_name = normalize_family(name)
|
||||||
|
else:
|
||||||
|
norm_name = name
|
||||||
|
return norm_name in getattr(space, child.tag, {})
|
||||||
|
|
||||||
|
def _translate_in_space(self, name, family, variable, namespace):
|
||||||
|
if not isinstance(family, self.family): # pylint: disable=E1101
|
||||||
|
if variable.tag in ['family', 'family_action']:
|
||||||
|
norm_name = normalize_family(name)
|
||||||
|
else:
|
||||||
|
norm_name = name
|
||||||
|
return getattr(family, variable.tag)[norm_name]
|
||||||
|
if namespace == 'creole':
|
||||||
|
path = name
|
||||||
|
else:
|
||||||
|
path = family.path + '.' + name
|
||||||
|
old_family_name = self.paths.get_variable_family_name(path)
|
||||||
|
if normalize_family(family.name) == old_family_name:
|
||||||
|
return getattr(family, variable.tag)[name]
|
||||||
|
old_family = self.space.variables['creole'].family[old_family_name] # pylint: disable=E1101
|
||||||
|
variable_obj = old_family.variable[name]
|
||||||
|
del old_family.variable[name]
|
||||||
|
if 'variable' not in vars(family):
|
||||||
|
family.variable = OrderedDict()
|
||||||
|
family.variable[name] = variable_obj
|
||||||
|
self.paths.append('variable', name, namespace, family.name, variable_obj)
|
||||||
|
return variable_obj
|
||||||
|
|
||||||
|
def remove_check(self, name): # pylint: disable=C0111
|
||||||
|
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'check'):
|
||||||
|
remove_checks = []
|
||||||
|
for idx, check in enumerate(self.space.constraints.check): # pylint: disable=E1101
|
||||||
|
if hasattr(check, 'target') and check.target == name:
|
||||||
|
remove_checks.append(idx)
|
||||||
|
|
||||||
|
remove_checks = list(set(remove_checks))
|
||||||
|
remove_checks.sort(reverse=True)
|
||||||
|
for idx in remove_checks:
|
||||||
|
self.space.constraints.check.pop(idx) # pylint: disable=E1101
|
||||||
|
def remove_condition(self, name): # pylint: disable=C0111
|
||||||
|
for idx, condition in enumerate(self.space.constraints.condition): # pylint: disable=E1101
|
||||||
|
remove_targets = []
|
||||||
|
if hasattr(condition, 'target'):
|
||||||
|
for target_idx, target in enumerate(condition.target):
|
||||||
|
if target.name == name:
|
||||||
|
remove_targets.append(target_idx)
|
||||||
|
remove_targets = list(set(remove_targets))
|
||||||
|
remove_targets.sort(reverse=True)
|
||||||
|
for idx in remove_targets:
|
||||||
|
del condition.target[idx]
|
||||||
|
|
||||||
|
def create_or_update_space_object(self, subspace, space, child, namespace):
|
||||||
|
"""Creates or retrieves the space object that corresponds
|
||||||
|
to the `child` XML object
|
||||||
|
|
||||||
|
Two attributes of the `child` XML object are important:
|
||||||
|
|
||||||
|
- with the `redefine` boolean flag attribute we know whether
|
||||||
|
the corresponding space object shall be created or updated
|
||||||
|
|
||||||
|
- `True` means that the corresponding space object shall be updated
|
||||||
|
- `False` means that the corresponding space object shall be created
|
||||||
|
|
||||||
|
- with the `exists` boolean flag attribute we know whether
|
||||||
|
the corresponding space object shall be created
|
||||||
|
(or nothing -- that is the space object isn't modified)
|
||||||
|
|
||||||
|
- `True` means that the corresponding space object shall be created
|
||||||
|
- `False` means that the corresponding space object is not updated
|
||||||
|
|
||||||
|
In the special case `redefine` is True and `exists` is False,
|
||||||
|
we create the corresponding space object if it doesn't exist
|
||||||
|
and we update it if it exists.
|
||||||
|
|
||||||
|
:return: the corresponding space object of the `child` XML object
|
||||||
"""
|
"""
|
||||||
if child.text is None or child.tag in self.forced_text_elts_as_name:
|
if child.tag in self.forced_text_elts_as_name:
|
||||||
return
|
name = child.text
|
||||||
text = child.text.strip()
|
else:
|
||||||
|
name = subspace['name']
|
||||||
|
if self._is_already_exists(name, space, child, namespace):
|
||||||
|
if child.tag in FORCE_REDEFINABLES:
|
||||||
|
redefine = self._convert_boolean(subspace.get('redefine', True))
|
||||||
|
else:
|
||||||
|
redefine = self._convert_boolean(subspace.get('redefine', False))
|
||||||
|
exists = self._convert_boolean(subspace.get('exists', True))
|
||||||
|
if redefine is True:
|
||||||
|
return self._translate_in_space(name, space, child, namespace)
|
||||||
|
elif exists is False:
|
||||||
|
raise SpaceObjShallNotBeUpdated()
|
||||||
|
else:
|
||||||
|
raise CreoleDictConsistencyError(_('Already present in another XML file, {} '
|
||||||
|
'cannot be re-created').format(name))
|
||||||
|
else:
|
||||||
|
redefine = self._convert_boolean(subspace.get('redefine', False))
|
||||||
|
exists = self._convert_boolean(subspace.get('exists', False))
|
||||||
|
if redefine is False or exists is True:
|
||||||
|
return getattr(self, child.tag)()
|
||||||
|
else:
|
||||||
|
raise CreoleDictConsistencyError(_('Redefined object: '
|
||||||
|
'{} does not exist yet').format(name))
|
||||||
|
|
||||||
|
def generate_creoleobj(self, child, space, namespace):
|
||||||
|
"""
|
||||||
|
instanciates or creates Creole Object Subspace objects
|
||||||
|
"""
|
||||||
|
if issubclass(getattr(self, child.tag), self.Redefinable):
|
||||||
|
creoleobj = self.create_or_update_space_object(child.attrib, space, child, namespace)
|
||||||
|
else:
|
||||||
|
# instanciates an object from the CreoleObjSpace's builtins types
|
||||||
|
# example : child.tag = constraints -> a self.Constraints() object is created
|
||||||
|
creoleobj = getattr(self, child.tag)()
|
||||||
|
# this Atom instance has to be a singleton here
|
||||||
|
# we do not re-create it, we reuse it
|
||||||
|
if isinstance(creoleobj, self.Atom) and child.tag in vars(space):
|
||||||
|
creoleobj = getattr(space, child.tag)
|
||||||
|
self.create_tree_structure(space, child, creoleobj)
|
||||||
|
return creoleobj
|
||||||
|
|
||||||
|
def create_tree_structure(self, space, child, creoleobj): # pylint: disable=R0201
|
||||||
|
"""
|
||||||
|
Builds the tree structure of the object space here
|
||||||
|
we set services attributes in order to be populated later on
|
||||||
|
for example::
|
||||||
|
|
||||||
|
space = Family()
|
||||||
|
space.variable = OrderedDict()
|
||||||
|
another example:
|
||||||
|
space = Variable()
|
||||||
|
space.value = list()
|
||||||
|
"""
|
||||||
|
if child.tag not in vars(space):
|
||||||
|
if isinstance(creoleobj, self.Redefinable):
|
||||||
|
setattr(space, child.tag, OrderedDict())
|
||||||
|
elif isinstance(creoleobj, self.UnRedefinable):
|
||||||
|
setattr(space, child.tag, [])
|
||||||
|
elif isinstance(creoleobj, self.Atom):
|
||||||
|
pass
|
||||||
|
else: # pragma: no cover
|
||||||
|
raise CreoleOperationError(_("Creole object {} "
|
||||||
|
"has a wrong type").format(type(creoleobj)))
|
||||||
|
|
||||||
|
def _add_to_tree_structure(self, creoleobj, space, child): # pylint: disable=R0201
|
||||||
|
if isinstance(creoleobj, self.Redefinable):
|
||||||
|
name = creoleobj.name
|
||||||
|
if child.tag == 'family' or child.tag == 'family_action':
|
||||||
|
name = normalize_family(name)
|
||||||
|
getattr(space, child.tag)[name] = creoleobj
|
||||||
|
elif isinstance(creoleobj, self.UnRedefinable):
|
||||||
|
getattr(space, child.tag).append(creoleobj)
|
||||||
|
else:
|
||||||
|
setattr(space, child.tag, creoleobj)
|
||||||
|
|
||||||
|
def _set_text_to_obj(self, child, creoleobj):
|
||||||
|
if child.text is None:
|
||||||
|
text = None
|
||||||
|
else:
|
||||||
|
text = child.text.strip()
|
||||||
if text:
|
if text:
|
||||||
variableobj.text = text
|
if child.tag in self.forced_text_elts_as_name:
|
||||||
|
creoleobj.name = text
|
||||||
|
else:
|
||||||
|
creoleobj.text = text
|
||||||
|
|
||||||
def set_attributes(self,
|
def _set_xml_attributes_to_obj(self, child, creoleobj):
|
||||||
xmlfile,
|
redefine = self._convert_boolean(child.attrib.get('redefine', False))
|
||||||
child,
|
has_value = hasattr(creoleobj, 'value')
|
||||||
variableobj,
|
if HIGH_COMPATIBILITY and has_value:
|
||||||
):
|
has_value = len(child) != 1 or child[0].text != None
|
||||||
""" set attributes to an object
|
if (redefine is True and child.tag == 'variable' and has_value
|
||||||
"""
|
and len(child) != 0):
|
||||||
redefine = self.convert_boolean(child.attrib.get('redefine', False))
|
del creoleobj.value
|
||||||
if redefine and child.tag == 'variable':
|
|
||||||
# delete old values
|
|
||||||
has_value = hasattr(variableobj, 'value')
|
|
||||||
if has_value and len(child) != 0:
|
|
||||||
del variableobj.value
|
|
||||||
for attr, val in child.attrib.items():
|
for attr, val in child.attrib.items():
|
||||||
if redefine and attr in UNREDEFINABLE:
|
if redefine and attr in UNREDEFINABLE:
|
||||||
msg = _(f'cannot redefine attribute "{attr}" for variable "{child.attrib["name"]}"'
|
# UNREDEFINABLE concerns only 'variable' node so we can fix name
|
||||||
f' in "{xmlfile}", already defined')
|
# to child.attrib['name']
|
||||||
raise DictConsistencyError(msg, 48, variableobj.xmlfiles[:-1])
|
name = child.attrib['name']
|
||||||
if attr in self.booleans_attributs:
|
raise CreoleDictConsistencyError(_("cannot redefine attribute {} for variable {}").format(attr, name))
|
||||||
val = self.convert_boolean(val)
|
if isinstance(getattr(creoleobj, attr, None), bool):
|
||||||
if attr == 'name' and getattr(variableobj, 'name', None):
|
if val == 'False':
|
||||||
# do not redefine name
|
val = False
|
||||||
|
elif val == 'True':
|
||||||
|
val = True
|
||||||
|
else: # pragma: no cover
|
||||||
|
raise CreoleOperationError(_('value for {} must be True or False, '
|
||||||
|
'not {}').format(attr, val))
|
||||||
|
if not (attr == 'name' and getattr(creoleobj, 'name', None) != None):
|
||||||
|
setattr(creoleobj, attr, val)
|
||||||
|
keys = list(vars(creoleobj).keys())
|
||||||
|
for incompatible in INCOMPATIBLE_ATTRIBUTES:
|
||||||
|
found = False
|
||||||
|
for inc in incompatible:
|
||||||
|
if inc in keys:
|
||||||
|
if found:
|
||||||
|
raise CreoleDictConsistencyError(_('those attributes are incompatible {}').format(incompatible))
|
||||||
|
found = True
|
||||||
|
|
||||||
|
|
||||||
|
def _creoleobj_tree_visitor(self, child, creoleobj, namespace):
|
||||||
|
"""Creole object tree manipulations
|
||||||
|
"""
|
||||||
|
if child.tag == 'variable' and child.attrib.get('remove_check', False):
|
||||||
|
self.remove_check(creoleobj.name)
|
||||||
|
if child.tag == 'variable' and child.attrib.get('remove_condition', False):
|
||||||
|
self.remove_condition(creoleobj.name)
|
||||||
|
if child.tag in ['auto', 'fill', 'check']:
|
||||||
|
variable_name = child.attrib['target']
|
||||||
|
# XXX not working with variable not in creole and in leader/followers
|
||||||
|
if variable_name in self.redefine_variables:
|
||||||
|
creoleobj.redefine = True
|
||||||
|
else:
|
||||||
|
creoleobj.redefine = False
|
||||||
|
if not hasattr(creoleobj, 'index'):
|
||||||
|
creoleobj.index = self.index
|
||||||
|
if child.tag in ['auto', 'fill', 'condition', 'check', 'action']:
|
||||||
|
creoleobj.namespace = namespace
|
||||||
|
|
||||||
|
def xml_parse_document(self, document, space, namespace, is_in_family=False):
|
||||||
|
"""Parses a Creole XML file
|
||||||
|
populates the CreoleObjSpace
|
||||||
|
"""
|
||||||
|
family_names = []
|
||||||
|
for child in document:
|
||||||
|
# this index enables us to reorder the 'fill' and 'auto' objects
|
||||||
|
self.index += 1
|
||||||
|
# doesn't proceed the XML commentaries
|
||||||
|
if not isinstance(child.tag, str):
|
||||||
continue
|
continue
|
||||||
setattr(variableobj, attr, val)
|
if child.tag == 'family':
|
||||||
|
is_in_family = True
|
||||||
|
if child.attrib['name'] in family_names:
|
||||||
|
raise CreoleDictConsistencyError(_('Family {} is set several times').format(child.attrib['name']))
|
||||||
|
family_names.append(child.attrib['name'])
|
||||||
|
if child.tag == 'variables':
|
||||||
|
child.attrib['name'] = namespace
|
||||||
|
if HIGH_COMPATIBILITY and child.tag == 'value' and child.text == None:
|
||||||
|
continue
|
||||||
|
# creole objects creation
|
||||||
|
try:
|
||||||
|
creoleobj = self.generate_creoleobj(child, space, namespace)
|
||||||
|
except SpaceObjShallNotBeUpdated:
|
||||||
|
continue
|
||||||
|
self._set_text_to_obj(child, creoleobj)
|
||||||
|
self._set_xml_attributes_to_obj(child, creoleobj)
|
||||||
|
self._creoleobj_tree_visitor(child, creoleobj, namespace)
|
||||||
|
self._fill_creoleobj_path_attribute(space, child, namespace, document, creoleobj)
|
||||||
|
self._add_to_tree_structure(creoleobj, space, child)
|
||||||
|
if list(child) != []:
|
||||||
|
self.xml_parse_document(child, creoleobj, namespace, is_in_family)
|
||||||
|
|
||||||
def remove(self,
|
def _fill_creoleobj_path_attribute(self, space, child, namespace, document, creoleobj): # pylint: disable=R0913
|
||||||
child,
|
|
||||||
variableobj,
|
|
||||||
redefine_variables,
|
|
||||||
):
|
|
||||||
"""Rougail object tree manipulations
|
|
||||||
"""
|
|
||||||
if child.tag == 'variable':
|
|
||||||
if child.attrib.get('remove_check', False):
|
|
||||||
self.remove_check(variableobj.name)
|
|
||||||
if child.attrib.get('remove_condition', False):
|
|
||||||
self.remove_condition(variableobj.name)
|
|
||||||
if child.attrib.get('remove_fill', False):
|
|
||||||
self.remove_fill(variableobj.name)
|
|
||||||
if child.tag == 'fill':
|
|
||||||
for target in child:
|
|
||||||
if target.tag == 'target' and target.text in redefine_variables:
|
|
||||||
self.remove_fill(target.text)
|
|
||||||
|
|
||||||
def remove_check(self, name):
|
|
||||||
"""Remove a check with a specified target
|
|
||||||
"""
|
|
||||||
remove_checks = []
|
|
||||||
for idx, check in enumerate(self.space.constraints.check): # pylint: disable=E1101
|
|
||||||
for target in check.target:
|
|
||||||
if target.name == name:
|
|
||||||
remove_checks.append(idx)
|
|
||||||
remove_checks.sort(reverse=True)
|
|
||||||
for idx in remove_checks:
|
|
||||||
self.space.constraints.check.pop(idx) # pylint: disable=E1101
|
|
||||||
|
|
||||||
def remove_condition(self,
|
|
||||||
name: str,
|
|
||||||
) -> None:
|
|
||||||
"""Remove a condition with a specified source
|
|
||||||
"""
|
|
||||||
remove_conditions = []
|
|
||||||
for idx, condition in enumerate(self.space.constraints.condition): # pylint: disable=E1101
|
|
||||||
if condition.source == name:
|
|
||||||
remove_conditions.append(idx)
|
|
||||||
remove_conditions.sort(reverse=True)
|
|
||||||
for idx in remove_conditions:
|
|
||||||
del self.space.constraints.condition[idx] # pylint: disable=E1101
|
|
||||||
|
|
||||||
def remove_fill(self,
|
|
||||||
name: str,
|
|
||||||
) -> None:
|
|
||||||
"""Remove a fill with a specified target
|
|
||||||
"""
|
|
||||||
remove_fills = []
|
|
||||||
for idx, fill in enumerate(self.space.constraints.fill): # pylint: disable=E1101
|
|
||||||
for target in fill.target:
|
|
||||||
if target.name == name:
|
|
||||||
remove_fills.append(idx)
|
|
||||||
remove_fills.sort(reverse=True)
|
|
||||||
for idx in remove_fills:
|
|
||||||
self.space.constraints.fill.pop(idx) # pylint: disable=E1101
|
|
||||||
|
|
||||||
def set_path(self,
|
|
||||||
namespace,
|
|
||||||
document,
|
|
||||||
variableobj,
|
|
||||||
):
|
|
||||||
"""Fill self.paths attributes
|
"""Fill self.paths attributes
|
||||||
"""
|
"""
|
||||||
if isinstance(variableobj, self.variable): # pylint: disable=E1101
|
if not isinstance(space, self.help): # pylint: disable=E1101
|
||||||
family_name = normalize_family(document.attrib['name'])
|
if child.tag == 'variable':
|
||||||
self.paths.add_variable(namespace,
|
family_name = normalize_family(document.attrib['name'])
|
||||||
normalize_family(variableobj.name),
|
self.paths.append('variable', child.attrib['name'], namespace, family_name,
|
||||||
namespace + '.' + family_name,
|
creoleobj)
|
||||||
document.attrib.get('dynamic') is not None,
|
if child.attrib.get('redefine', 'False') == 'True':
|
||||||
variableobj,
|
if namespace == 'creole':
|
||||||
)
|
self.redefine_variables.append(child.attrib['name'])
|
||||||
elif isinstance(variableobj, self.family): # pylint: disable=E1101
|
else:
|
||||||
family_name = normalize_family(variableobj.name)
|
self.redefine_variables.append(namespace + '.' + family_name + '.' +
|
||||||
if namespace != Config['variable_namespace']:
|
child.attrib['name'])
|
||||||
family_name = namespace + '.' + family_name
|
|
||||||
self.paths.add_family(namespace,
|
|
||||||
family_name,
|
|
||||||
variableobj,
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
if child.tag == 'family':
|
||||||
def add_to_tree_structure(variableobj,
|
family_name = normalize_family(child.attrib['name'])
|
||||||
space,
|
if namespace != 'creole':
|
||||||
child,
|
family_name = namespace + '.' + family_name
|
||||||
namespace: str,
|
self.paths.append('family', family_name, namespace, creoleobj=creoleobj)
|
||||||
) -> None:
|
creoleobj.path = self.paths.get_family_path(family_name, namespace)
|
||||||
"""add a variable to the tree
|
|
||||||
|
def create_or_populate_from_xml(self, namespace, xmlfolders, from_zephir=None):
|
||||||
|
"""Parses a bunch of XML files
|
||||||
|
populates the CreoleObjSpace
|
||||||
"""
|
"""
|
||||||
variableobj.namespace = namespace
|
documents = self.xmlreflector.load_xml_from_folders(xmlfolders, from_zephir)
|
||||||
if isinstance(variableobj, Redefinable):
|
for xmlfile, document in documents:
|
||||||
name = variableobj.name
|
try:
|
||||||
if child.tag in ['family', 'variable']:
|
self.redefine_variables = []
|
||||||
name = normalize_family(name)
|
self.xml_parse_document(document, self.space, namespace)
|
||||||
getattr(space, child.tag)[name] = variableobj
|
except Exception as err:
|
||||||
elif isinstance(variableobj, UnRedefinable):
|
#print(_('error in XML file {}').format(xmlfile))
|
||||||
getattr(space, child.tag).append(variableobj)
|
raise err
|
||||||
|
|
||||||
|
def populate_from_zephir(self, namespace, xmlfile):
|
||||||
|
self.redefine_variables = []
|
||||||
|
document = self.xmlreflector.parse_xmlfile(xmlfile, from_zephir=True, zephir2=True)
|
||||||
|
self.xml_parse_document(document, self.space, namespace)
|
||||||
|
|
||||||
|
def space_visitor(self, eosfunc_file): # pylint: disable=C0111
|
||||||
|
ActionAnnotator(self)
|
||||||
|
ServiceAnnotator(self)
|
||||||
|
SpaceAnnotator(self, eosfunc_file)
|
||||||
|
|
||||||
|
def save(self, filename, force_no_save=False):
|
||||||
|
"""Save an XML output on disk
|
||||||
|
|
||||||
|
:param filename: the full XML filename
|
||||||
|
"""
|
||||||
|
xml = Element('creole')
|
||||||
|
self._xml_export(xml, self.space)
|
||||||
|
if not force_no_save:
|
||||||
|
self.xmlreflector.save_xmlfile(filename, xml)
|
||||||
|
return xml
|
||||||
|
|
||||||
|
def save_probes(self, filename, force_no_save=False):
|
||||||
|
"""Save an XML output on disk
|
||||||
|
|
||||||
|
:param filename: the full XML filename
|
||||||
|
"""
|
||||||
|
ret = {}
|
||||||
|
for variable in self.probe_variables:
|
||||||
|
args = []
|
||||||
|
kwargs = {}
|
||||||
|
if hasattr(variable, 'param'):
|
||||||
|
for param in variable.param:
|
||||||
|
list_param = list(vars(param).keys())
|
||||||
|
if 'index' in list_param:
|
||||||
|
list_param.remove('index')
|
||||||
|
if list_param == ['text']:
|
||||||
|
args.append(param.text)
|
||||||
|
elif list_param == ['text', 'name']:
|
||||||
|
kwargs[param.name] = param.text
|
||||||
|
else:
|
||||||
|
print(vars(param))
|
||||||
|
raise Exception('hu?')
|
||||||
|
ret[variable.target] = {'function': variable.name,
|
||||||
|
'args': args,
|
||||||
|
'kwargs': kwargs}
|
||||||
|
if not force_no_save:
|
||||||
|
with open(filename, 'w') as fhj:
|
||||||
|
dump(ret, fhj)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def _get_attributes(self, space): # pylint: disable=R0201
|
||||||
|
for attr in dir(space):
|
||||||
|
if not attr.startswith('_'):
|
||||||
|
yield attr
|
||||||
|
|
||||||
|
def _sub_xml_export(self, name, node, node_name, space, current_space):
|
||||||
|
if isinstance(space, dict):
|
||||||
|
space = list(space.values())
|
||||||
|
if isinstance(space, list):
|
||||||
|
for subspace in space:
|
||||||
|
if isinstance(subspace, self.Leadership):
|
||||||
|
_name = 'leader'
|
||||||
|
else:
|
||||||
|
_name = name
|
||||||
|
if name in ['services', 'variables', 'actions']:
|
||||||
|
_name = 'family'
|
||||||
|
if HIGH_COMPATIBILITY and not hasattr(subspace, 'doc'):
|
||||||
|
subspace.doc = ''
|
||||||
|
if _name == 'value' and (not hasattr(subspace, 'name') or subspace.name is None):
|
||||||
|
continue
|
||||||
|
child_node = SubElement(node, _name)
|
||||||
|
self._xml_export(child_node, subspace, _name)
|
||||||
|
elif isinstance(space, self.Atom):
|
||||||
|
if name == 'services':
|
||||||
|
child_node = SubElement(node, 'family')
|
||||||
|
child_node.attrib['name'] = name
|
||||||
|
else:
|
||||||
|
child_node = SubElement(node, name)
|
||||||
|
for subname in self._get_attributes(space):
|
||||||
|
subspace = getattr(space, subname)
|
||||||
|
self._sub_xml_export(subname, child_node, name, subspace, space)
|
||||||
|
elif isinstance(space, self.Redefinable):
|
||||||
|
child_node = SubElement(node, 'family')
|
||||||
|
child_node.attrib['name'] = name
|
||||||
|
for subname in self._get_attributes(space):
|
||||||
|
subspace = getattr(space, subname)
|
||||||
|
self._sub_xml_export(subname, child_node, name, subspace, space)
|
||||||
else:
|
else:
|
||||||
setattr(space, child.tag, variableobj)
|
# FIXME plutot dans annotator ...
|
||||||
|
if name in PROPERTIES and node.tag in ['variable', 'family', 'leader']:
|
||||||
|
if space is True:
|
||||||
|
for prop in CONVERT_PROPERTIES.get(name, [name]):
|
||||||
|
SubElement(node, 'property').text = prop
|
||||||
|
|
||||||
|
elif name not in ERASED_ATTRIBUTES:
|
||||||
|
if name == 'name' and node_name in self.forced_text_elts_as_name and not hasattr(current_space, 'param'):
|
||||||
|
if isinstance(space, str):
|
||||||
|
node.text = space
|
||||||
|
else:
|
||||||
|
node.text = str(space)
|
||||||
|
elif name == 'text' and node_name in self.forced_text_elts:
|
||||||
|
node.text = space
|
||||||
|
elif node.tag == 'family' and name == 'name':
|
||||||
|
if 'doc' not in node.attrib.keys():
|
||||||
|
node.attrib['doc'] = space
|
||||||
|
node.attrib['name'] = normalize_family(space, check_name=False)
|
||||||
|
elif node.tag in ['variable', 'family', 'leader'] and name == 'mode':
|
||||||
|
if space is not None:
|
||||||
|
SubElement(node, 'property').text = space
|
||||||
|
else:
|
||||||
|
if name in RENAME_ATTIBUTES:
|
||||||
|
name = RENAME_ATTIBUTES[name]
|
||||||
|
if space is not None:
|
||||||
|
node.attrib[name] = str(space)
|
||||||
|
|
||||||
|
def _xml_export(self, node, space, node_name='creole'):
|
||||||
|
for name in self._get_attributes(space):
|
||||||
|
subspace = getattr(space, name)
|
||||||
|
self._sub_xml_export(name, node, node_name, subspace, space)
|
||||||
|
|
||||||
|
|
||||||
|
class Path(object):
|
||||||
|
"""Helper class to handle the `path` attribute of a CreoleObjSpace
|
||||||
|
instance.
|
||||||
|
|
||||||
|
sample: path="creole.general.condition"
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.variables = {}
|
||||||
|
self.families = {}
|
||||||
|
|
||||||
|
def append(self, pathtype, name, namespace, family=None, creoleobj=None): # pylint: disable=C0111
|
||||||
|
if pathtype == 'family':
|
||||||
|
self.families[name] = dict(name=name, namespace=namespace, creoleobj=creoleobj)
|
||||||
|
elif pathtype == 'variable':
|
||||||
|
if namespace == 'creole':
|
||||||
|
varname = name
|
||||||
|
else:
|
||||||
|
if '.' in name:
|
||||||
|
varname = name
|
||||||
|
else:
|
||||||
|
varname = '.'.join([namespace, family, name])
|
||||||
|
self.variables[varname] = dict(name=name, family=family, namespace=namespace,
|
||||||
|
leader=None, creoleobj=creoleobj)
|
||||||
|
else: # pragma: no cover
|
||||||
|
raise Exception('unknown pathtype {}'.format(pathtype))
|
||||||
|
|
||||||
|
def get_family_path(self, name, current_namespace): # pylint: disable=C0111
|
||||||
|
if current_namespace is None: # pragma: no cover
|
||||||
|
raise CreoleOperationError('current_namespace must not be None')
|
||||||
|
dico = self.families[normalize_family(name,
|
||||||
|
check_name=False,
|
||||||
|
allow_dot=True)]
|
||||||
|
if dico['namespace'] != 'creole' and current_namespace != dico['namespace']:
|
||||||
|
raise CreoleDictConsistencyError(_('A family located in the {} namespace '
|
||||||
|
'shall not be used in the {} namespace').format(
|
||||||
|
dico['namespace'], current_namespace))
|
||||||
|
path = dico['name']
|
||||||
|
if dico['namespace'] is not None and '.' not in dico['name']:
|
||||||
|
path = '.'.join([dico['namespace'], path])
|
||||||
|
return path
|
||||||
|
|
||||||
|
def get_family_namespace(self, name): # pylint: disable=C0111
|
||||||
|
dico = self.families[name]
|
||||||
|
if dico['namespace'] is None:
|
||||||
|
return dico['name']
|
||||||
|
return dico['namespace']
|
||||||
|
|
||||||
|
def get_family_obj(self, name): # pylint: disable=C0111
|
||||||
|
if name not in self.families:
|
||||||
|
raise CreoleDictConsistencyError(_('unknown family {}').format(name))
|
||||||
|
dico = self.families[name]
|
||||||
|
return dico['creoleobj']
|
||||||
|
|
||||||
|
def get_variable_name(self, name): # pylint: disable=C0111
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
return dico['name']
|
||||||
|
|
||||||
|
def get_variable_obj(self, name): # pylint: disable=C0111
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
return dico['creoleobj']
|
||||||
|
|
||||||
|
def get_variable_family_name(self, name): # pylint: disable=C0111
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
return dico['family']
|
||||||
|
|
||||||
|
def get_variable_family_path(self, name): # pylint: disable=C0111
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
list_path = [dico['namespace'], dico['family']]
|
||||||
|
if dico['leader'] is not None:
|
||||||
|
list_path.append(dico['leader'])
|
||||||
|
return '.'.join(list_path)
|
||||||
|
|
||||||
|
def get_variable_namespace(self, name): # pylint: disable=C0111
|
||||||
|
return self._get_variable(name)['namespace']
|
||||||
|
|
||||||
|
def get_variable_path(self, name, current_namespace, allow_source=False): # pylint: disable=C0111
|
||||||
|
if current_namespace is None: # pragma: no cover
|
||||||
|
raise CreoleOperationError('current_namespace must not be None')
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
if not allow_source:
|
||||||
|
if dico['namespace'] not in ['creole', 'services'] and current_namespace != dico['namespace']:
|
||||||
|
raise CreoleDictConsistencyError(_('A variable located in the {} namespace '
|
||||||
|
'shall not be used in the {} namespace').format(
|
||||||
|
dico['namespace'], current_namespace))
|
||||||
|
if '.' in dico['name']:
|
||||||
|
return dico['name']
|
||||||
|
list_path = [dico['namespace'], dico['family']]
|
||||||
|
if dico['leader'] is not None:
|
||||||
|
list_path.append(dico['leader'])
|
||||||
|
list_path.append(dico['name'])
|
||||||
|
return '.'.join(list_path)
|
||||||
|
|
||||||
|
def path_is_defined(self, name): # pylint: disable=C0111
|
||||||
|
return name in self.variables
|
||||||
|
|
||||||
|
def set_leader(self, name, leader): # pylint: disable=C0111
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
namespace = dico['namespace']
|
||||||
|
if dico['leader'] != None:
|
||||||
|
raise CreoleDictConsistencyError(_('Already defined leader {} for variable'
|
||||||
|
' {}'.format(dico['leader'], name)))
|
||||||
|
dico['leader'] = leader
|
||||||
|
if namespace != 'creole':
|
||||||
|
new_path = self.get_variable_path(name, namespace)
|
||||||
|
self.append('variable', new_path, namespace, family=dico['family'], creoleobj=dico['creoleobj'])
|
||||||
|
self.variables[new_path]['leader'] = leader
|
||||||
|
del self.variables[name]
|
||||||
|
|
||||||
|
def _get_variable(self, name):
|
||||||
|
if name not in self.variables:
|
||||||
|
if name.startswith('creole.'):
|
||||||
|
name = name.split('.')[-1]
|
||||||
|
if name not in self.variables:
|
||||||
|
raise CreoleDictConsistencyError(_('unknown option {}').format(name))
|
||||||
|
return self.variables[name]
|
||||||
|
|
||||||
|
def get_leader(self, name): # pylint: disable=C0111
|
||||||
|
dico = self._get_variable(name)
|
||||||
|
return dico['leader']
|
||||||
|
@ -1,223 +0,0 @@
|
|||||||
"""Manage path to find objects
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from .i18n import _
|
|
||||||
from .error import DictConsistencyError
|
|
||||||
from .config import Config
|
|
||||||
from .utils import normalize_family
|
|
||||||
|
|
||||||
|
|
||||||
class Path:
|
|
||||||
"""Helper class to handle the `path` attribute of a CreoleObjSpace
|
|
||||||
instance.
|
|
||||||
|
|
||||||
sample: path="creole.general.condition"
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
self.variables = {}
|
|
||||||
self.families = {}
|
|
||||||
self.full_paths_families = {}
|
|
||||||
self.full_paths_variables = {}
|
|
||||||
|
|
||||||
# Family
|
|
||||||
def add_family(self,
|
|
||||||
namespace: str,
|
|
||||||
name: str,
|
|
||||||
variableobj: str,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""Add a new family
|
|
||||||
"""
|
|
||||||
if namespace == Config['variable_namespace']:
|
|
||||||
full_name = '.'.join([namespace, name])
|
|
||||||
self.full_paths_families[name] = full_name
|
|
||||||
else:
|
|
||||||
if '.' not in name: # pragma: no cover
|
|
||||||
msg = _(f'Variable "{name}" in namespace "{namespace}" must have dot')
|
|
||||||
raise DictConsistencyError(msg, 39, variableobj.xmlfiles)
|
|
||||||
full_name = name
|
|
||||||
if full_name in self.families and \
|
|
||||||
self.families[full_name]['variableobj'] != variableobj: # pragma: no cover
|
|
||||||
raise DictConsistencyError(_(f'Duplicate family name "{name}"'), 37, variableobj.xmlfiles)
|
|
||||||
self.families[full_name] = dict(name=name,
|
|
||||||
namespace=namespace,
|
|
||||||
variableobj=variableobj,
|
|
||||||
)
|
|
||||||
variableobj.path = full_name
|
|
||||||
|
|
||||||
def add_leadership(self,
|
|
||||||
namespace: str,
|
|
||||||
path: str,
|
|
||||||
variableobj: str,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""add a new leadership
|
|
||||||
"""
|
|
||||||
self.families[path] = dict(name=path,
|
|
||||||
namespace=namespace,
|
|
||||||
variableobj=variableobj,
|
|
||||||
)
|
|
||||||
variableobj.path = path
|
|
||||||
|
|
||||||
def get_family(self,
|
|
||||||
name: str,
|
|
||||||
current_namespace: str,
|
|
||||||
) -> 'Family': # pylint: disable=C0111
|
|
||||||
"""Get a family
|
|
||||||
"""
|
|
||||||
name = '.'.join([normalize_family(subname) for subname in name.split('.')])
|
|
||||||
if name not in self.families and name in self.full_paths_families:
|
|
||||||
name = self.full_paths_families[name]
|
|
||||||
if name not in self.families:
|
|
||||||
raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
|
|
||||||
dico = self.families[name]
|
|
||||||
if current_namespace not in [Config['variable_namespace'], 'services'] and \
|
|
||||||
current_namespace != dico['namespace']:
|
|
||||||
msg = _(f'A family located in the "{dico["namespace"]}" namespace '
|
|
||||||
f'shall not be used in the "{current_namespace}" namespace')
|
|
||||||
raise DictConsistencyError(msg, 38, [])
|
|
||||||
return dico['variableobj']
|
|
||||||
|
|
||||||
# Leadership
|
|
||||||
def set_leader(self,
|
|
||||||
namespace: str,
|
|
||||||
leader_family_name: str,
|
|
||||||
leadership_name: str,
|
|
||||||
name: str,
|
|
||||||
) -> None: # pylint: disable=C0111
|
|
||||||
"""set a variable a leadership member
|
|
||||||
"""
|
|
||||||
# need rebuild path and move object in new path
|
|
||||||
old_path = leader_family_name + '.' + name
|
|
||||||
leadership_path = leader_family_name + '.' + leadership_name
|
|
||||||
new_path = leadership_path + '.' + name
|
|
||||||
self.variables[new_path] = self.variables.pop(old_path)
|
|
||||||
self.variables[new_path]['leader'] = leadership_path
|
|
||||||
self.variables[new_path]['variableobj'].path = new_path
|
|
||||||
self.variables[new_path]['family'] = leadership_path
|
|
||||||
if namespace == Config['variable_namespace']:
|
|
||||||
self.full_paths_variables[name] = new_path
|
|
||||||
|
|
||||||
def is_leader(self, path): # pylint: disable=C0111
|
|
||||||
"""Is the variable is a leader
|
|
||||||
"""
|
|
||||||
variable = self._get_variable(path)
|
|
||||||
if not variable['leader']:
|
|
||||||
return False
|
|
||||||
leadership = self.get_family(variable['leader'], variable['variableobj'].namespace)
|
|
||||||
return leadership.variable[0].path == path
|
|
||||||
|
|
||||||
# Variable
|
|
||||||
def add_variable(self, # pylint: disable=R0913
|
|
||||||
namespace: str,
|
|
||||||
name: str,
|
|
||||||
family: str,
|
|
||||||
is_dynamic: bool,
|
|
||||||
variableobj,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""Add a new variable (with path)
|
|
||||||
"""
|
|
||||||
if '.' not in name:
|
|
||||||
full_path = '.'.join([family, name])
|
|
||||||
if namespace == Config['variable_namespace']:
|
|
||||||
self.full_paths_variables[name] = full_path
|
|
||||||
else:
|
|
||||||
full_path = name
|
|
||||||
variableobj.path = full_path
|
|
||||||
self.variables[full_path] = dict(name=name,
|
|
||||||
family=family,
|
|
||||||
leader=None,
|
|
||||||
is_dynamic=is_dynamic,
|
|
||||||
variableobj=variableobj,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_variable(self,
|
|
||||||
name: str,
|
|
||||||
) -> 'Variable': # pylint: disable=C0111
|
|
||||||
"""Get variable object from a path
|
|
||||||
"""
|
|
||||||
variable, suffix = self._get_variable(name, with_suffix=True)
|
|
||||||
if suffix:
|
|
||||||
raise DictConsistencyError(_(f"{name} is a dynamic variable"), 36, [])
|
|
||||||
return variable['variableobj']
|
|
||||||
|
|
||||||
def get_variable_family_path(self,
|
|
||||||
name: str,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""Get the full path of a family
|
|
||||||
"""
|
|
||||||
return self._get_variable(name)['family']
|
|
||||||
|
|
||||||
def get_variable_path(self,
|
|
||||||
name: str,
|
|
||||||
current_namespace: str,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""get full path of a variable
|
|
||||||
"""
|
|
||||||
dico, suffix = self._get_variable(name,
|
|
||||||
with_suffix=True,
|
|
||||||
)
|
|
||||||
namespace = dico['variableobj'].namespace
|
|
||||||
if namespace not in [Config['variable_namespace'], 'services'] and \
|
|
||||||
current_namespace != namespace:
|
|
||||||
msg = _(f'A variable located in the "{namespace}" namespace shall not be used '
|
|
||||||
f'in the "{current_namespace}" namespace')
|
|
||||||
raise DictConsistencyError(msg, 41, [])
|
|
||||||
return dico['variableobj'].path, suffix
|
|
||||||
|
|
||||||
def path_is_defined(self,
|
|
||||||
path: str,
|
|
||||||
) -> str: # pylint: disable=C0111
|
|
||||||
"""The path is a valid path
|
|
||||||
"""
|
|
||||||
if '.' not in path and path not in self.variables and path in self.full_paths_variables:
|
|
||||||
return True
|
|
||||||
return path in self.variables
|
|
||||||
|
|
||||||
def variable_is_dynamic(self,
|
|
||||||
name: str,
|
|
||||||
) -> bool:
|
|
||||||
"""This variable is in dynamic family
|
|
||||||
"""
|
|
||||||
return self._get_variable(name)['is_dynamic']
|
|
||||||
|
|
||||||
def _get_variable(self,
|
|
||||||
name: str,
|
|
||||||
with_suffix: bool=False,
|
|
||||||
) -> str:
|
|
||||||
name = '.'.join([normalize_family(subname) for subname in name.split('.')])
|
|
||||||
if name not in self.variables:
|
|
||||||
if '.' not in name and name in self.full_paths_variables:
|
|
||||||
name = self.full_paths_variables[name]
|
|
||||||
elif with_suffix:
|
|
||||||
for var_name, full_path in self.full_paths_variables.items():
|
|
||||||
if name.startswith(var_name):
|
|
||||||
variable = self._get_variable(full_path)
|
|
||||||
if variable['is_dynamic']:
|
|
||||||
return variable, name[len(var_name):]
|
|
||||||
if name not in self.variables:
|
|
||||||
raise DictConsistencyError(_(f'unknown option {name}'), 42, [])
|
|
||||||
if with_suffix:
|
|
||||||
return self.variables[name], None
|
|
||||||
return self.variables[name]
|
|
@ -1,94 +0,0 @@
|
|||||||
"""Takes a bunch of Rougail XML dispatched in differents folders
|
|
||||||
as an input and outputs a Tiramisu's file.
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sample usage::
|
|
||||||
|
|
||||||
>>> from rougail import Rougail
|
|
||||||
>>> rougail = Rougail('/usr/share/rougail/rougail.dtd')
|
|
||||||
>>> rougail.create_or_populate_from_xml(['/usr/share/rougail/dicos'])
|
|
||||||
>>> rougail.create_or_populate_from_xml(['/usr/share/rougail/extra1'], 'extra1')
|
|
||||||
>>> rougail.space_visitor('/usr/share/rougail/funcs.py')
|
|
||||||
>>> tiramisu = rougail.save()
|
|
||||||
|
|
||||||
The Rougail
|
|
||||||
|
|
||||||
- loads the XML into an internal RougailObjSpace representation
|
|
||||||
- visits/annotates the objects
|
|
||||||
- dumps the object space as Tiramisu string
|
|
||||||
|
|
||||||
The visit/annotation stage is a complex step that corresponds to the Rougail
|
|
||||||
procedures.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
from .objspace import RougailObjSpace
|
|
||||||
from .xmlreflector import XMLReflector
|
|
||||||
from .tiramisureflector import TiramisuReflector
|
|
||||||
from .annotator import SpaceAnnotator
|
|
||||||
|
|
||||||
|
|
||||||
class Rougail:
|
|
||||||
"""Rougail object
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
dtdfilename: str,
|
|
||||||
) -> None:
|
|
||||||
self.xmlreflector = XMLReflector()
|
|
||||||
self.xmlreflector.parse_dtd(dtdfilename)
|
|
||||||
self.rougailobjspace = RougailObjSpace(self.xmlreflector)
|
|
||||||
self.funcs_path = None
|
|
||||||
|
|
||||||
def create_or_populate_from_xml(self,
|
|
||||||
xmlfolders: List[str],
|
|
||||||
namespace: str=None,
|
|
||||||
) -> List[str]:
|
|
||||||
"""Parses a bunch of XML files and populates the RougailObjSpace
|
|
||||||
"""
|
|
||||||
for xmlfile in self.xmlreflector.load_xml_from_folders(xmlfolders):
|
|
||||||
document = self.xmlreflector.parse_xmlfile(xmlfile)
|
|
||||||
self.rougailobjspace.redefine_variables = []
|
|
||||||
self.rougailobjspace.xml_parse_document(xmlfile,
|
|
||||||
document,
|
|
||||||
self.rougailobjspace.space,
|
|
||||||
namespace,
|
|
||||||
)
|
|
||||||
|
|
||||||
def space_visitor(self,
|
|
||||||
eosfunc_file: str,
|
|
||||||
) -> None:
|
|
||||||
"""All XML are loader, now annotate content
|
|
||||||
"""
|
|
||||||
self.funcs_path = eosfunc_file
|
|
||||||
SpaceAnnotator(self.rougailobjspace, eosfunc_file)
|
|
||||||
|
|
||||||
def save(self) -> str:
|
|
||||||
"""Return tiramisu object declaration as a string
|
|
||||||
"""
|
|
||||||
tiramisu_objects = TiramisuReflector(self.rougailobjspace,
|
|
||||||
self.funcs_path,
|
|
||||||
)
|
|
||||||
return tiramisu_objects.get_text() + '\n'
|
|
@ -1,61 +1,94 @@
|
|||||||
"""Template langage for Rougail
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
Created by:
|
Gestion du mini-langage de template
|
||||||
EOLE (http://eole.orion.education.fr)
|
On travaille sur les fichiers cibles
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import imp
|
||||||
|
import sys
|
||||||
from shutil import copy
|
from shutil import copy
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
from os import listdir, makedirs, getcwd, chdir
|
from os import listdir, unlink, makedirs
|
||||||
from os.path import dirname, join, isfile, abspath, normpath, relpath
|
from os.path import dirname, basename, join, split, isfile, isdir
|
||||||
|
|
||||||
|
from tempfile import mktemp
|
||||||
|
|
||||||
|
from Cheetah import Parser
|
||||||
|
|
||||||
|
|
||||||
|
# l'encoding du template est déterminé par une regexp (encodingDirectiveRE dans Parser.py)
|
||||||
|
# il cherche un ligne qui ressemble à '#encoding: utf-8
|
||||||
|
# cette classe simule le module 're' et retourne toujours l'encoding utf-8
|
||||||
|
# 6224
|
||||||
|
class FakeEncoding:
|
||||||
|
def groups(self):
|
||||||
|
return ('utf-8',)
|
||||||
|
|
||||||
|
def search(self, *args):
|
||||||
|
return self
|
||||||
|
Parser.encodingDirectiveRE = FakeEncoding()
|
||||||
|
|
||||||
|
|
||||||
from Cheetah.Template import Template as ChtTemplate
|
from Cheetah.Template import Template as ChtTemplate
|
||||||
from Cheetah.NameMapper import NotFound as CheetahNotFound
|
from Cheetah.NameMapper import NotFound as CheetahNotFound
|
||||||
|
|
||||||
try:
|
from tiramisu import Config
|
||||||
from tiramisu3 import Config
|
from tiramisu.error import PropertiesOptionError
|
||||||
from tiramisu3.error import PropertiesOptionError # pragma: no cover
|
|
||||||
except ModuleNotFoundError: # pragma: no cover
|
|
||||||
from tiramisu import Config
|
|
||||||
from tiramisu.error import PropertiesOptionError
|
|
||||||
|
|
||||||
from .config import Config
|
from .config import patch_dir
|
||||||
from .error import FileNotFound, TemplateError
|
from .error import FileNotFound, TemplateError, TemplateDisabled
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .utils import normalize_family, load_modules
|
from .utils import normalize_family
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.addHandler(logging.NullHandler())
|
log.addHandler(logging.NullHandler())
|
||||||
|
|
||||||
|
class IsDefined:
|
||||||
|
"""
|
||||||
|
filtre permettant de ne pas lever d'exception au cas où
|
||||||
|
la variable Creole n'est pas définie
|
||||||
|
"""
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def __call__(self, varname):
|
||||||
|
if '.' in varname:
|
||||||
|
splitted_var = varname.split('.')
|
||||||
|
if len(splitted_var) != 2:
|
||||||
|
msg = _("Group variables must be of type master.slave")
|
||||||
|
raise KeyError(msg)
|
||||||
|
master, slave = splitted_var
|
||||||
|
if master in self.context:
|
||||||
|
return slave in self.context[master].slave.keys()
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return varname in self.context
|
||||||
|
|
||||||
|
|
||||||
|
class CreoleGet:
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def __call__(self, varname):
|
||||||
|
return self.context[varname]
|
||||||
|
|
||||||
|
def __getitem__(self, varname):
|
||||||
|
"""For bracket and dotted notation
|
||||||
|
"""
|
||||||
|
return self.context[varname]
|
||||||
|
|
||||||
|
def __contains__(self, varname):
|
||||||
|
"""Check variable existence in context
|
||||||
|
"""
|
||||||
|
return varname in self.context
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cl_compile(kls, *args, **kwargs):
|
def cl_compile(kls, *args, **kwargs):
|
||||||
"""Rewrite compile methode to force some settings
|
|
||||||
"""
|
|
||||||
kwargs['compilerSettings'] = {'directiveStartToken' : '%',
|
kwargs['compilerSettings'] = {'directiveStartToken' : '%',
|
||||||
'cheetahVarStartToken' : '%%',
|
'cheetahVarStartToken' : '%%',
|
||||||
'EOLSlurpToken' : '%',
|
'EOLSlurpToken' : '%',
|
||||||
@ -65,379 +98,346 @@ def cl_compile(kls, *args, **kwargs):
|
|||||||
'commentEndToken' : 'µ' * 10,
|
'commentEndToken' : 'µ' * 10,
|
||||||
'multiLineCommentStartToken' : 'µ' * 10,
|
'multiLineCommentStartToken' : 'µ' * 10,
|
||||||
'multiLineCommentEndToken' : 'µ' * 10}
|
'multiLineCommentEndToken' : 'µ' * 10}
|
||||||
return kls.old_compile(*args, **kwargs) # pylint: disable=E1101
|
return kls.old_compile(*args, **kwargs)
|
||||||
ChtTemplate.old_compile = ChtTemplate.compile
|
ChtTemplate.old_compile = ChtTemplate.compile
|
||||||
ChtTemplate.compile = cl_compile
|
ChtTemplate.compile = cl_compile
|
||||||
|
|
||||||
|
|
||||||
class CheetahTemplate(ChtTemplate): # pylint: disable=W0223
|
class CreoleClient:
|
||||||
"""Construct a cheetah templating object
|
def __init__(self,
|
||||||
|
config: Config):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
|
||||||
|
class CheetahTemplate(ChtTemplate):
|
||||||
|
"""classe pour personnaliser et faciliter la construction
|
||||||
|
du template Cheetah
|
||||||
"""
|
"""
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
filename: str,
|
filename: str,
|
||||||
context,
|
context,
|
||||||
eosfunc: Dict,
|
eosfunc: Dict,
|
||||||
extra_context: Dict,
|
destfilename,
|
||||||
):
|
variable):
|
||||||
"""Initialize Creole CheetahTemplate
|
"""Initialize Creole CheetahTemplate
|
||||||
"""
|
"""
|
||||||
|
extra_context = {'is_defined' : IsDefined(context),
|
||||||
|
'normalize_family': normalize_family,
|
||||||
|
'rougail_filename': destfilename
|
||||||
|
}
|
||||||
|
if variable:
|
||||||
|
extra_context['rougail_variable'] = variable
|
||||||
ChtTemplate.__init__(self,
|
ChtTemplate.__init__(self,
|
||||||
file=filename,
|
file=filename,
|
||||||
searchList=[context, eosfunc, extra_context])
|
searchList=[context, eosfunc, extra_context])
|
||||||
|
|
||||||
# FORK of Cheetah function, do not replace '\\' by '/'
|
|
||||||
def serverSidePath(self,
|
|
||||||
path=None,
|
|
||||||
normpath=normpath,
|
|
||||||
abspath=abspath
|
|
||||||
): # pylint: disable=W0621
|
|
||||||
|
|
||||||
# strange...
|
class CreoleLeader:
|
||||||
if path is None and isinstance(self, str):
|
def __init__(self, value, slave=None, index=None):
|
||||||
path = self
|
"""
|
||||||
if path: # pylint: disable=R1705
|
On rend la variable itérable pour pouvoir faire:
|
||||||
return normpath(abspath(path))
|
for ip in iplist:
|
||||||
# original code return normpath(abspath(path.replace("\\", '/')))
|
print(ip.network)
|
||||||
elif hasattr(self, '_filePath') and self._filePath: # pragma: no cover
|
print(ip.netmask)
|
||||||
return normpath(abspath(self._filePath))
|
print(ip)
|
||||||
else: # pragma: no cover
|
index is used for CreoleLint
|
||||||
return None
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CreoleLeaderIndex:
|
|
||||||
"""This object is create when access to a specified Index of the variable
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
value,
|
|
||||||
follower,
|
|
||||||
index,
|
|
||||||
) -> None:
|
|
||||||
self._value = value
|
self._value = value
|
||||||
self._follower = follower
|
if slave is not None:
|
||||||
|
self.slave = slave
|
||||||
|
else:
|
||||||
|
self.slave = {}
|
||||||
self._index = index
|
self._index = index
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name not in self._follower:
|
"""Get slave variable or attribute of master value.
|
||||||
raise AttributeError()
|
|
||||||
value = self._follower[name]
|
|
||||||
if isinstance(value, PropertiesOptionError):
|
|
||||||
raise AttributeError()
|
|
||||||
return value
|
|
||||||
|
|
||||||
def __str__(self):
|
If the attribute is a name of a slave variable, return its value.
|
||||||
return str(self._value)
|
Otherwise, returns the requested attribute of master value.
|
||||||
|
"""
|
||||||
|
if name in self.slave:
|
||||||
|
value = self.slave[name]
|
||||||
|
if isinstance(value, PropertiesOptionError):
|
||||||
|
raise AttributeError()
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return getattr(self._value, name)
|
||||||
|
|
||||||
def __lt__(self, value):
|
def __getitem__(self, index):
|
||||||
return self._value.__lt__(value)
|
"""Get a master.slave at requested index.
|
||||||
|
"""
|
||||||
|
ret = {}
|
||||||
|
for key, values in self.slave.items():
|
||||||
|
ret[key] = values[index]
|
||||||
|
return CreoleLeader(self._value[index], ret, index)
|
||||||
|
|
||||||
def __le__(self, value):
|
def __iter__(self):
|
||||||
return self._value.__le__(value)
|
"""Iterate over master.slave.
|
||||||
|
|
||||||
|
Return synchronised value of master.slave.
|
||||||
|
"""
|
||||||
|
for i in range(len(self._value)):
|
||||||
|
ret = {}
|
||||||
|
for key, values in self.slave.items():
|
||||||
|
ret[key] = values[i]
|
||||||
|
yield CreoleLeader(self._value[i], ret, i)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Delegate to master value
|
||||||
|
"""
|
||||||
|
return len(self._value)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Show CreoleLeader as dictionary.
|
||||||
|
|
||||||
|
The master value is stored under 'value' key.
|
||||||
|
The slaves are stored under 'slave' key.
|
||||||
|
"""
|
||||||
|
return repr({'value': self._value, 'slave': self.slave})
|
||||||
|
|
||||||
def __eq__(self, value):
|
def __eq__(self, value):
|
||||||
return self._value.__eq__(value)
|
return value == self._value
|
||||||
|
|
||||||
def __ne__(self, value):
|
def __ne__(self, value):
|
||||||
return self._value.__ne__(value)
|
return value != self._value
|
||||||
|
|
||||||
|
def __lt__(self, value):
|
||||||
|
return self._value < value
|
||||||
|
|
||||||
|
def __le__(self, value):
|
||||||
|
return self._value <= value
|
||||||
|
|
||||||
def __gt__(self, value):
|
def __gt__(self, value):
|
||||||
return self._value.__gt__(value)
|
return self._value > value
|
||||||
|
|
||||||
def __ge__(self, value):
|
def __ge__(self, value):
|
||||||
return self._value >= value
|
return self._value >= value
|
||||||
|
|
||||||
def __add__(self, value):
|
def __str__(self):
|
||||||
return self._value.__add__(value)
|
"""Delegate to master value
|
||||||
|
|
||||||
def __radd__(self, value):
|
|
||||||
return value + self._value
|
|
||||||
|
|
||||||
|
|
||||||
class CreoleLeader:
|
|
||||||
"""Implement access to leader and follower variable
|
|
||||||
For examples: %%leader, %%leader[0].follower1
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
value,
|
|
||||||
) -> None:
|
|
||||||
self._value = value
|
|
||||||
self._follower = {}
|
|
||||||
|
|
||||||
def __getitem__(self, index):
|
|
||||||
"""Get a leader.follower at requested index.
|
|
||||||
"""
|
"""
|
||||||
followers = {key: values[index] for key, values in self._follower.items()}
|
return str(self._value)
|
||||||
return CreoleLeaderIndex(self._value[index],
|
|
||||||
followers,
|
|
||||||
index,
|
|
||||||
)
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __add__(self, val):
|
||||||
"""Iterate over leader.follower.
|
return self._value.__add__(val)
|
||||||
|
|
||||||
Return synchronised value of leader.follower.
|
def __radd__(self, val):
|
||||||
"""
|
return val + self._value
|
||||||
for index in range(len(self._value)):
|
|
||||||
yield self.__getitem__(index)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __contains__(self, item):
|
||||||
return len(self._value)
|
return item in self._value
|
||||||
|
|
||||||
def __contains__(self, value):
|
async def add_slave(self, config, name, path):
|
||||||
return self._value.__contains__(value)
|
if isinstance(self._value, list):
|
||||||
|
values = []
|
||||||
async def add_follower(self,
|
for idx in range(len(self._value)):
|
||||||
config,
|
try:
|
||||||
name: str,
|
values.append(await config.option(path, idx).value.get())
|
||||||
path: str,
|
except PropertiesOptionError as err:
|
||||||
):
|
values.append(err)
|
||||||
"""Add a new follower
|
else:
|
||||||
"""
|
raise Exception('hu?')
|
||||||
self._follower[name] = []
|
self.slave[name] = values
|
||||||
for index in range(len(self._value)):
|
|
||||||
try:
|
|
||||||
value = await config.option(path, index).value.get()
|
|
||||||
except PropertiesOptionError as err:
|
|
||||||
value = err
|
|
||||||
self._follower[name].append(value)
|
|
||||||
|
|
||||||
|
|
||||||
class CreoleExtra:
|
class CreoleExtra:
|
||||||
"""Object that implement access to extra variable
|
|
||||||
For example %%extra1.family.variable
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
suboption: Dict) -> None:
|
suboption: Dict) -> None:
|
||||||
self.suboption = suboption
|
self.suboption = suboption
|
||||||
|
|
||||||
def __getattr__(self,
|
def __getattr__(self,
|
||||||
key: str,
|
key: str) -> Any:
|
||||||
) -> Any:
|
|
||||||
return self.suboption[key]
|
return self.suboption[key]
|
||||||
|
|
||||||
def __iter__(self):
|
def __repr__(self):
|
||||||
return iter(self.suboption.values())
|
return self.suboption.__str__()
|
||||||
|
|
||||||
|
|
||||||
class CreoleTemplateEngine:
|
class CreoleTemplateEngine:
|
||||||
"""Engine to process Creole cheetah template
|
"""Engine to process Creole cheetah template
|
||||||
"""
|
"""
|
||||||
def __init__(self, # pylint: disable=R0913
|
def __init__(self,
|
||||||
config: Config,
|
config: Config,
|
||||||
eosfunc_file: str,
|
eosfunc_file: str,
|
||||||
distrib_dir: str,
|
distrib_dir: str,
|
||||||
tmp_dir: str,
|
tmp_dir: str,
|
||||||
dest_dir: str,
|
dest_dir:str) -> None:
|
||||||
) -> None:
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.dest_dir = dest_dir
|
self.dest_dir = dest_dir
|
||||||
self.tmp_dir = tmp_dir
|
self.tmp_dir = tmp_dir
|
||||||
self.distrib_dir = distrib_dir
|
self.distrib_dir = distrib_dir
|
||||||
eos = {}
|
eos = {}
|
||||||
if eosfunc_file is not None:
|
if eosfunc_file is not None:
|
||||||
eosfunc = load_modules(eosfunc_file)
|
eosfunc = imp.load_source('eosfunc', eosfunc_file)
|
||||||
for func in dir(eosfunc):
|
for func in dir(eosfunc):
|
||||||
if not func.startswith('_'):
|
if not func.startswith('_'):
|
||||||
eos[func] = getattr(eosfunc, func)
|
eos[func] = getattr(eosfunc, func)
|
||||||
self.eosfunc = eos
|
self.eosfunc = eos
|
||||||
self.rougail_variables_dict = {}
|
self.creole_variables_dict = {}
|
||||||
|
|
||||||
def patch_template(self,
|
async def load_eole_variables_creole(self,
|
||||||
filename: str,
|
optiondescription):
|
||||||
tmp_dir: str,
|
|
||||||
patch_dir: str,
|
|
||||||
) -> None:
|
|
||||||
"""Apply patch to a template
|
|
||||||
"""
|
|
||||||
patch_cmd = ['patch', '-d', tmp_dir, '-N', '-p1']
|
|
||||||
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
|
|
||||||
|
|
||||||
patch_file = join(patch_dir, f'{filename}.patch')
|
|
||||||
if isfile(patch_file):
|
|
||||||
log.info(_("Patching template '{filename}' with '{patch_file}'"))
|
|
||||||
rel_patch_file = relpath(patch_file, tmp_dir)
|
|
||||||
ret = call(patch_cmd + patch_no_debug + ['-i', rel_patch_file])
|
|
||||||
if ret: # pragma: no cover
|
|
||||||
patch_cmd_err = ' '.join(patch_cmd + ['-i', rel_patch_file])
|
|
||||||
msg = _(f"Error applying patch: '{rel_patch_file}'\n"
|
|
||||||
f"To reproduce and fix this error {patch_cmd_err}")
|
|
||||||
log.error(_(msg))
|
|
||||||
copy(join(self.distrib_dir, filename), tmp_dir)
|
|
||||||
|
|
||||||
def prepare_template(self,
|
|
||||||
filename: str,
|
|
||||||
tmp_dir: str,
|
|
||||||
patch_dir: str,
|
|
||||||
) -> None:
|
|
||||||
"""Prepare template source file
|
|
||||||
"""
|
|
||||||
log.info(_("Copy template: '{filename}' -> '{tmp_dir}'"))
|
|
||||||
copy(filename, tmp_dir)
|
|
||||||
self.patch_template(filename, tmp_dir, patch_dir)
|
|
||||||
|
|
||||||
def process(self,
|
|
||||||
source: str,
|
|
||||||
true_destfilename: str,
|
|
||||||
destfilename: str,
|
|
||||||
variable: Any,
|
|
||||||
):
|
|
||||||
"""Process a cheetah template
|
|
||||||
"""
|
|
||||||
# full path of the destination file
|
|
||||||
log.info(_(f"Cheetah processing: '{destfilename}'"))
|
|
||||||
try:
|
|
||||||
extra_context = {'normalize_family': normalize_family,
|
|
||||||
'rougail_filename': true_destfilename
|
|
||||||
}
|
|
||||||
if variable:
|
|
||||||
extra_context['rougail_variable'] = variable
|
|
||||||
cheetah_template = CheetahTemplate(source,
|
|
||||||
self.rougail_variables_dict,
|
|
||||||
self.eosfunc,
|
|
||||||
extra_context,
|
|
||||||
)
|
|
||||||
data = str(cheetah_template)
|
|
||||||
except CheetahNotFound as err: # pragma: no cover
|
|
||||||
varname = err.args[0][13:-1]
|
|
||||||
msg = f"Error: unknown variable used in template {source} to {destfilename}: {varname}"
|
|
||||||
raise TemplateError(_(msg)) from err
|
|
||||||
except Exception as err: # pragma: no cover
|
|
||||||
msg = _(f"Error while instantiating template {source} to {destfilename}: {err}")
|
|
||||||
raise TemplateError(msg) from err
|
|
||||||
|
|
||||||
with open(destfilename, 'w') as file_h:
|
|
||||||
file_h.write(data)
|
|
||||||
|
|
||||||
def instance_file(self,
|
|
||||||
filevar: Dict,
|
|
||||||
tmp_dir: str,
|
|
||||||
dest_dir: str,
|
|
||||||
) -> None:
|
|
||||||
"""Run templatisation on one file
|
|
||||||
"""
|
|
||||||
log.info(_("Instantiating file '{filename}'"))
|
|
||||||
if 'variable' in filevar:
|
|
||||||
variable = filevar['variable']
|
|
||||||
else:
|
|
||||||
variable = None
|
|
||||||
filenames = filevar['name']
|
|
||||||
if not isinstance(filenames, list):
|
|
||||||
filenames = [filenames]
|
|
||||||
if variable:
|
|
||||||
variable = [variable]
|
|
||||||
for idx, filename in enumerate(filenames):
|
|
||||||
destfilename = join(dest_dir, filename[1:])
|
|
||||||
makedirs(dirname(destfilename), exist_ok=True)
|
|
||||||
if variable:
|
|
||||||
var = variable[idx]
|
|
||||||
else:
|
|
||||||
var = None
|
|
||||||
source = join(tmp_dir, filevar['source'])
|
|
||||||
if filevar['templating']:
|
|
||||||
self.process(source,
|
|
||||||
filename,
|
|
||||||
destfilename,
|
|
||||||
var,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
copy(source, destfilename)
|
|
||||||
|
|
||||||
async def instance_files(self) -> None:
|
|
||||||
"""Run templatisation on all files
|
|
||||||
"""
|
|
||||||
ori_dir = getcwd()
|
|
||||||
tmp_dir = relpath(self.tmp_dir, self.distrib_dir)
|
|
||||||
dest_dir = relpath(self.dest_dir, self.distrib_dir)
|
|
||||||
patch_dir = relpath(Config['patch_dir'], self.distrib_dir)
|
|
||||||
chdir(self.distrib_dir)
|
|
||||||
for option in await self.config.option.list(type='all'):
|
|
||||||
namespace = await option.option.name()
|
|
||||||
if namespace == Config['variable_namespace']:
|
|
||||||
await self.load_variables_namespace(option)
|
|
||||||
else:
|
|
||||||
self.rougail_variables_dict[namespace] = await self.load_variables_extra(option)
|
|
||||||
for template in listdir('.'):
|
|
||||||
self.prepare_template(template, tmp_dir, patch_dir)
|
|
||||||
for service_obj in await self.config.option('services').list('all'):
|
|
||||||
for fills in await service_obj.list('all'):
|
|
||||||
if await fills.option.name() in ['files', 'overrides']:
|
|
||||||
for fill_obj in await fills.list('all'):
|
|
||||||
fill = await fill_obj.value.dict()
|
|
||||||
filename = fill['source']
|
|
||||||
if not isfile(filename): # pragma: no cover
|
|
||||||
raise FileNotFound(_(f"File {filename} does not exist."))
|
|
||||||
if fill['activate']:
|
|
||||||
self.instance_file(fill,
|
|
||||||
tmp_dir,
|
|
||||||
dest_dir,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
log.debug(_("Instantiation of file '{filename}' disabled"))
|
|
||||||
chdir(ori_dir)
|
|
||||||
|
|
||||||
async def load_variables_namespace (self,
|
|
||||||
optiondescription,
|
|
||||||
):
|
|
||||||
"""load variables from the "variable namespace
|
|
||||||
"""
|
|
||||||
for option in await optiondescription.list('all'):
|
for option in await optiondescription.list('all'):
|
||||||
if await option.option.isoptiondescription():
|
if await option.option.isoptiondescription():
|
||||||
if await option.option.isleadership():
|
if await option.option.isleadership():
|
||||||
for idx, suboption in enumerate(await option.list('all')):
|
for idx, suboption in enumerate(await option.list('all')):
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
leader = CreoleLeader(await suboption.value.get())
|
leader = CreoleLeader(await suboption.value.get())
|
||||||
self.rougail_variables_dict[await suboption.option.name()] = leader
|
self.creole_variables_dict[await suboption.option.name()] = leader
|
||||||
else:
|
else:
|
||||||
await leader.add_follower(self.config,
|
await leader.add_slave(self.config,
|
||||||
await suboption.option.name(),
|
await suboption.option.name(),
|
||||||
await suboption.option.path(),
|
await suboption.option.path())
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
await self.load_variables_namespace(option)
|
await self.load_eole_variables_creole(option)
|
||||||
else:
|
else:
|
||||||
self.rougail_variables_dict[await option.option.name()] = await option.value.get()
|
self.creole_variables_dict[await option.option.name()] = await option.value.get()
|
||||||
|
|
||||||
async def load_variables_extra(self,
|
async def load_eole_variables(self,
|
||||||
optiondescription,
|
namespace,
|
||||||
) -> CreoleExtra:
|
optiondescription):
|
||||||
"""Load all variables and set it in CreoleExtra objects
|
|
||||||
"""
|
|
||||||
families = {}
|
families = {}
|
||||||
for family in await optiondescription.list('all'):
|
for family in await optiondescription.list('all'):
|
||||||
variables = {}
|
variables = {}
|
||||||
for variable in await family.list('all'):
|
for variable in await family.list('all'):
|
||||||
if await variable.option.isoptiondescription():
|
if await variable.option.isoptiondescription() and await variable.option.isleadership():
|
||||||
if await variable.option.isleadership():
|
for idx, suboption in enumerate(await variable.list('all')):
|
||||||
for idx, suboption in enumerate(await variable.list('all')):
|
if idx == 0:
|
||||||
if idx == 0:
|
leader = CreoleLeader(await suboption.value.get())
|
||||||
leader = CreoleLeader(await suboption.value.get())
|
leader_name = await suboption.option.name()
|
||||||
leader_name = await suboption.option.name()
|
else:
|
||||||
else:
|
await leader.add_slave(self.config,
|
||||||
await leader.add_follower(self.config,
|
await suboption.option.name(),
|
||||||
await suboption.option.name(),
|
await suboption.option.path())
|
||||||
await suboption.option.path(),
|
variables[leader_name] = leader
|
||||||
)
|
|
||||||
variables[leader_name] = leader
|
|
||||||
else:
|
|
||||||
subfamilies = await self.load_variables_extra(variable)
|
|
||||||
variables[await variable.option.name()] = subfamilies
|
|
||||||
else:
|
else:
|
||||||
variables[await variable.option.name()] = await variable.value.get()
|
variables[await variable.option.name()] = await variable.value.get()
|
||||||
families[await family.option.name()] = CreoleExtra(variables)
|
families[await family.option.name()] = CreoleExtra(variables)
|
||||||
return CreoleExtra(families)
|
self.creole_variables_dict[namespace] = CreoleExtra(families)
|
||||||
|
|
||||||
|
def patch_template(self,
|
||||||
|
filename: str):
|
||||||
|
"""Apply patch to a template
|
||||||
|
"""
|
||||||
|
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1']
|
||||||
|
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
|
||||||
|
|
||||||
|
# patches variante + locaux
|
||||||
|
for directory in [join(patch_dir, 'variante'), patch_dir]:
|
||||||
|
patch_file = join(directory, f'{filename}.patch')
|
||||||
|
if isfile(patch_file):
|
||||||
|
log.info(_("Patching template '{filename}' with '{patch_file}'"))
|
||||||
|
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file])
|
||||||
|
if ret:
|
||||||
|
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file])
|
||||||
|
log.error(_(f"Error applying patch: '{patch_file}'\nTo reproduce and fix this error {patch_cmd_err}"))
|
||||||
|
copy(filename, self.tmp_dir)
|
||||||
|
|
||||||
|
def prepare_template(self,
|
||||||
|
filename: str):
|
||||||
|
"""Prepare template source file
|
||||||
|
"""
|
||||||
|
log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'"))
|
||||||
|
copy(filename, self.tmp_dir)
|
||||||
|
self.patch_template(filename)
|
||||||
|
|
||||||
|
def process(self,
|
||||||
|
destfilename: str,
|
||||||
|
filevar: Dict,
|
||||||
|
variable: Any):
|
||||||
|
"""Process a cheetah template
|
||||||
|
"""
|
||||||
|
# full path of the destination file
|
||||||
|
log.info(_(f"Cheetah processing: '{destfilename}'"))
|
||||||
|
try:
|
||||||
|
cheetah_template = CheetahTemplate(join(self.tmp_dir,
|
||||||
|
filevar['source']),
|
||||||
|
self.creole_variables_dict,
|
||||||
|
self.eosfunc,
|
||||||
|
destfilename,
|
||||||
|
variable)
|
||||||
|
data = str(cheetah_template)
|
||||||
|
except CheetahNotFound as err:
|
||||||
|
varname = err.args[0][13:-1]
|
||||||
|
raise TemplateError(_(f"Error: unknown variable used in template {destfilename} : {varname}"))
|
||||||
|
except Exception as err:
|
||||||
|
raise TemplateError(_(f"Error while instantiating template {destfilename}: {err}"))
|
||||||
|
|
||||||
|
with open(destfilename, 'w') as file_h:
|
||||||
|
file_h.write(data)
|
||||||
|
|
||||||
|
def instance_file(self,
|
||||||
|
filevar: Dict,
|
||||||
|
systemd_rights: list) -> None:
|
||||||
|
"""Run templatisation on one file
|
||||||
|
"""
|
||||||
|
log.info(_("Instantiating file '{filename}'"))
|
||||||
|
filenames = filevar['name']
|
||||||
|
if 'variable' in filevar:
|
||||||
|
variable = filevar['variable']
|
||||||
|
else:
|
||||||
|
variable = None
|
||||||
|
if not isinstance(filenames, list):
|
||||||
|
filenames = [filenames]
|
||||||
|
if variable:
|
||||||
|
variable = [variable]
|
||||||
|
for idx, filename in enumerate(filenames):
|
||||||
|
destfilename = join(self.dest_dir,
|
||||||
|
filename[1:])
|
||||||
|
makedirs(dirname(destfilename), exist_ok=True)
|
||||||
|
if variable:
|
||||||
|
var = variable[idx]
|
||||||
|
else:
|
||||||
|
var = None
|
||||||
|
self.process(destfilename,
|
||||||
|
filevar,
|
||||||
|
var)
|
||||||
|
systemd_rights.append(f'C {filename} {filevar["mode"]} {filevar["owner"]} {filevar["group"]} - -')
|
||||||
|
systemd_rights.append(f'z {filename} - - - - -')
|
||||||
|
|
||||||
|
async def instance_files(self) -> None:
|
||||||
|
"""Run templatisation on all files
|
||||||
|
"""
|
||||||
|
for option in await self.config.option.list(type='all'):
|
||||||
|
namespace = await option.option.name()
|
||||||
|
if namespace in ['services', 'actions']:
|
||||||
|
continue
|
||||||
|
elif namespace == 'creole':
|
||||||
|
await self.load_eole_variables_creole(option)
|
||||||
|
else:
|
||||||
|
await self.load_eole_variables(namespace,
|
||||||
|
option)
|
||||||
|
for template in listdir(self.distrib_dir):
|
||||||
|
self.prepare_template(join(self.distrib_dir, template))
|
||||||
|
systemd_rights = []
|
||||||
|
for service_obj in await self.config.option('services').list('all'):
|
||||||
|
for fills in await service_obj.list('all'):
|
||||||
|
if await fills.option.name() == 'files':
|
||||||
|
for fill_obj in await fills.list('all'):
|
||||||
|
fill = await fill_obj.value.dict()
|
||||||
|
filename = fill['source']
|
||||||
|
distib_file = join(self.distrib_dir, filename)
|
||||||
|
if not isfile(distib_file):
|
||||||
|
raise FileNotFound(_(f"File {distib_file} does not exist."))
|
||||||
|
if fill.get('activate', False):
|
||||||
|
self.instance_file(fill,
|
||||||
|
systemd_rights)
|
||||||
|
else:
|
||||||
|
log.debug(_("Instantiation of file '{filename}' disabled"))
|
||||||
|
|
||||||
|
with open(join(self.dest_dir, 'rougail.conf'), 'w') as fh:
|
||||||
|
fh.write('\n'.join(systemd_rights))
|
||||||
|
fh.write('\n')
|
||||||
|
|
||||||
|
|
||||||
async def generate(config: Config,
|
async def generate(config: Config,
|
||||||
eosfunc_file: str,
|
eosfunc_file: str,
|
||||||
distrib_dir: str,
|
distrib_dir: str,
|
||||||
tmp_dir: str,
|
tmp_dir: str,
|
||||||
dest_dir: str,
|
dest_dir: str) -> None:
|
||||||
) -> None:
|
|
||||||
"""Generate all files
|
|
||||||
"""
|
|
||||||
engine = CreoleTemplateEngine(config,
|
engine = CreoleTemplateEngine(config,
|
||||||
eosfunc_file,
|
eosfunc_file,
|
||||||
distrib_dir,
|
distrib_dir,
|
||||||
tmp_dir,
|
tmp_dir,
|
||||||
dest_dir,
|
dest_dir)
|
||||||
)
|
|
||||||
await engine.instance_files()
|
await engine.instance_files()
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
"""Redefine Tiramisu object
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from tiramisu3 import DynOptionDescription
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
from tiramisu import DynOptionDescription
|
|
||||||
from .utils import normalize_family
|
|
||||||
|
|
||||||
|
|
||||||
class ConvertDynOptionDescription(DynOptionDescription):
|
|
||||||
"""Suffix could be an integer, we should convert it in str
|
|
||||||
Suffix could also contain invalid character, so we should "normalize" it
|
|
||||||
"""
|
|
||||||
def convert_suffix_to_path(self, suffix):
|
|
||||||
if not isinstance(suffix, str):
|
|
||||||
suffix = str(suffix)
|
|
||||||
return normalize_family(suffix)
|
|
@ -1,382 +0,0 @@
|
|||||||
"""loader
|
|
||||||
flattened XML specific
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from json import dumps
|
|
||||||
|
|
||||||
from .config import Config
|
|
||||||
from .annotator import CONVERT_OPTION
|
|
||||||
from .objspace import RootRougailObject
|
|
||||||
|
|
||||||
|
|
||||||
class Root(): # pylint: disable=R0903
|
|
||||||
"""Root classes
|
|
||||||
"""
|
|
||||||
path = '.'
|
|
||||||
|
|
||||||
|
|
||||||
class BaseElt: # pylint: disable=R0903
|
|
||||||
"""Base element
|
|
||||||
"""
|
|
||||||
name = 'baseoption'
|
|
||||||
doc = 'baseoption'
|
|
||||||
path = '.'
|
|
||||||
|
|
||||||
|
|
||||||
class TiramisuReflector:
|
|
||||||
"""Convert object to tiramisu representation
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
objectspace,
|
|
||||||
funcs_path,
|
|
||||||
):
|
|
||||||
self.index = 0
|
|
||||||
self.text = ["from importlib.machinery import SourceFileLoader",
|
|
||||||
"from importlib.util import spec_from_loader, module_from_spec",
|
|
||||||
f"loader = SourceFileLoader('func', '{funcs_path}')",
|
|
||||||
"spec = spec_from_loader(loader.name, loader)",
|
|
||||||
"func = module_from_spec(spec)",
|
|
||||||
"loader.exec_module(func)",
|
|
||||||
"for key, value in dict(locals()).items():",
|
|
||||||
" if key != ['SourceFileLoader', 'func']:",
|
|
||||||
" setattr(func, key, value)",
|
|
||||||
"try:",
|
|
||||||
" from tiramisu3 import *",
|
|
||||||
"except:",
|
|
||||||
" from tiramisu import *",
|
|
||||||
"from rougail.tiramisu import ConvertDynOptionDescription",
|
|
||||||
]
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.make_tiramisu_objects()
|
|
||||||
|
|
||||||
def make_tiramisu_objects(self) -> None:
|
|
||||||
"""make tiramisu objects
|
|
||||||
"""
|
|
||||||
baseelt = BaseElt()
|
|
||||||
self.set_name(baseelt)
|
|
||||||
basefamily = Family(baseelt,
|
|
||||||
self.text,
|
|
||||||
self.objectspace,
|
|
||||||
)
|
|
||||||
for elt in self.reorder_family():
|
|
||||||
self.populate_family(basefamily,
|
|
||||||
elt,
|
|
||||||
)
|
|
||||||
# parse object
|
|
||||||
baseelt.reflector_object.get() # pylint: disable=E1101
|
|
||||||
|
|
||||||
def reorder_family(self):
|
|
||||||
"""variable_namespace family has to be loaded before any other family
|
|
||||||
because `extra` family could use `variable_namespace` variables.
|
|
||||||
"""
|
|
||||||
if hasattr(self.objectspace.space, 'variables'):
|
|
||||||
if Config['variable_namespace'] in self.objectspace.space.variables:
|
|
||||||
yield self.objectspace.space.variables[Config['variable_namespace']]
|
|
||||||
for elt, value in self.objectspace.space.variables.items():
|
|
||||||
if elt != Config['variable_namespace']:
|
|
||||||
yield value
|
|
||||||
if hasattr(self.objectspace.space, 'services'):
|
|
||||||
yield self.objectspace.space.services
|
|
||||||
|
|
||||||
def populate_family(self,
|
|
||||||
parent_family,
|
|
||||||
elt,
|
|
||||||
):
|
|
||||||
"""Populate family
|
|
||||||
"""
|
|
||||||
self.set_name(elt)
|
|
||||||
family = Family(elt,
|
|
||||||
self.text,
|
|
||||||
self.objectspace,
|
|
||||||
)
|
|
||||||
parent_family.add(family)
|
|
||||||
for children in vars(elt).values():
|
|
||||||
if isinstance(children, self.objectspace.family):
|
|
||||||
self.populate_family(family,
|
|
||||||
children,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
if isinstance(children, dict):
|
|
||||||
children = list(children.values())
|
|
||||||
if isinstance(children, list):
|
|
||||||
for child in children:
|
|
||||||
if isinstance(child, self.objectspace.property_) or \
|
|
||||||
not isinstance(child, RootRougailObject):
|
|
||||||
continue
|
|
||||||
if isinstance(child, self.objectspace.variable):
|
|
||||||
self.set_name(child)
|
|
||||||
family.add(Variable(child,
|
|
||||||
self.text,
|
|
||||||
self.objectspace,
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
self.populate_family(family,
|
|
||||||
child,
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_name(self,
|
|
||||||
elt,
|
|
||||||
):
|
|
||||||
"""Set name
|
|
||||||
"""
|
|
||||||
elt.reflector_name = f'option_{self.index}'
|
|
||||||
self.index += 1
|
|
||||||
|
|
||||||
def get_text(self):
|
|
||||||
"""Get text
|
|
||||||
"""
|
|
||||||
return '\n'.join(self.text)
|
|
||||||
|
|
||||||
|
|
||||||
class Common:
|
|
||||||
"""Common function for variable and family
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
elt,
|
|
||||||
text,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
self.elt = elt
|
|
||||||
self.option_name = None
|
|
||||||
self.text = text
|
|
||||||
self.objectspace = objectspace
|
|
||||||
self.elt.reflector_object = self
|
|
||||||
self.object_type = None
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
"""Get tiramisu's object
|
|
||||||
"""
|
|
||||||
if self.option_name is None:
|
|
||||||
self.option_name = self.elt.reflector_name
|
|
||||||
self.populate_attrib()
|
|
||||||
self.populate_informations()
|
|
||||||
return self.option_name
|
|
||||||
|
|
||||||
def populate_attrib(self):
|
|
||||||
"""Populate attributes
|
|
||||||
"""
|
|
||||||
keys = {'name': self.convert_str(self.elt.name)}
|
|
||||||
if hasattr(self.elt, 'doc'):
|
|
||||||
keys['doc'] = self.convert_str(self.elt.doc)
|
|
||||||
self._populate_attrib(keys)
|
|
||||||
if hasattr(self.elt, 'properties'):
|
|
||||||
keys['properties'] = self.properties_to_string(self.elt.properties)
|
|
||||||
attrib = ', '.join([f'{key}={value}' for key, value in keys.items()])
|
|
||||||
self.text.append(f'{self.option_name} = {self.object_type}({attrib})')
|
|
||||||
|
|
||||||
def _populate_attrib(self,
|
|
||||||
keys: dict,
|
|
||||||
) -> None: # pragma: no cover
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def convert_str(value):
|
|
||||||
"""convert string
|
|
||||||
"""
|
|
||||||
return dumps(value, ensure_ascii=False)
|
|
||||||
|
|
||||||
def properties_to_string(self,
|
|
||||||
values: list,
|
|
||||||
) -> None:
|
|
||||||
"""Change properties to string
|
|
||||||
"""
|
|
||||||
properties = [self.convert_str(property_) for property_ in values
|
|
||||||
if isinstance(property_, str)]
|
|
||||||
calc_properties = [self.calc_properties(property_) for property_ in values \
|
|
||||||
if isinstance(property_, self.objectspace.property_)]
|
|
||||||
return 'frozenset({' + ', '.join(sorted(properties) + calc_properties) + '})'
|
|
||||||
|
|
||||||
def calc_properties(self,
|
|
||||||
child,
|
|
||||||
) -> str:
|
|
||||||
"""Populate properties
|
|
||||||
"""
|
|
||||||
option_name = child.source.reflector_object.get()
|
|
||||||
kwargs = f"'condition': ParamOption({option_name}, todict=True), "
|
|
||||||
if child.expected is None or isinstance(child.expected, int):
|
|
||||||
kwargs += f"'expected': ParamValue({child.expected})"
|
|
||||||
elif isinstance(child.expected, str):
|
|
||||||
val = self.convert_str(child.expected)
|
|
||||||
kwargs += f"'expected': ParamValue({val})"
|
|
||||||
else:
|
|
||||||
val = child.expected.reflector_object.get()
|
|
||||||
kwargs += f"'expected': ParamOption({val})"
|
|
||||||
if child.inverse:
|
|
||||||
kwargs += ", 'reverse_condition': ParamValue(True)"
|
|
||||||
return (f"Calculation(func.calc_value, Params(ParamValue('{child.name}'), "
|
|
||||||
f"kwargs={{{kwargs}}}))")
|
|
||||||
|
|
||||||
def populate_informations(self):
|
|
||||||
"""Populate Tiramisu's informations
|
|
||||||
"""
|
|
||||||
if not hasattr(self.elt, 'information'):
|
|
||||||
return
|
|
||||||
for key, value in vars(self.elt.information).items():
|
|
||||||
if key == 'xmlfiles':
|
|
||||||
continue
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = self.convert_str(value)
|
|
||||||
self.text.append(f"{self.option_name}.impl_set_information('{key}', {value})")
|
|
||||||
|
|
||||||
|
|
||||||
class Variable(Common):
|
|
||||||
"""Manage variable
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
elt,
|
|
||||||
text,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
super().__init__(elt, text, objectspace)
|
|
||||||
self.object_type = CONVERT_OPTION[elt.type]['opttype']
|
|
||||||
|
|
||||||
def _populate_attrib(self,
|
|
||||||
keys: dict,
|
|
||||||
):
|
|
||||||
if hasattr(self.elt, 'opt'):
|
|
||||||
keys['opt'] = self.elt.opt.reflector_object.get()
|
|
||||||
if hasattr(self.elt, 'values'):
|
|
||||||
values = self.elt.values
|
|
||||||
if values[0].type == 'calculation':
|
|
||||||
value = values[0].name.reflector_object.get()
|
|
||||||
keys['values'] = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
|
|
||||||
else:
|
|
||||||
keys['values'] = str(tuple([val.name for val in values]))
|
|
||||||
if hasattr(self.elt, 'multi') and self.elt.multi:
|
|
||||||
keys['multi'] = self.elt.multi
|
|
||||||
for key in ['default', 'default_multi']:
|
|
||||||
if hasattr(self.elt, key) and getattr(self.elt, key) is not None:
|
|
||||||
value = getattr(self.elt, key)
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = self.convert_str(value)
|
|
||||||
elif isinstance(value, self.objectspace.value):
|
|
||||||
value = self.calculation_value(value, [])
|
|
||||||
keys[key] = value
|
|
||||||
if hasattr(self.elt, 'validators'):
|
|
||||||
keys['validators'] = '[' + ', '.join([self.calculation_value(val,
|
|
||||||
['ParamSelfOption()']) for val in self.elt.validators]) + ']'
|
|
||||||
for key in ['min_number', 'max_number']:
|
|
||||||
if hasattr(self.elt, key):
|
|
||||||
keys[key] = getattr(self.elt, key)
|
|
||||||
for key, value in CONVERT_OPTION[self.elt.type].get('initkwargs', {}).items():
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = f"'{value}'"
|
|
||||||
keys[key] = value
|
|
||||||
|
|
||||||
def calculation_value(self,
|
|
||||||
child,
|
|
||||||
args,
|
|
||||||
) -> str:
|
|
||||||
"""Generate calculated value
|
|
||||||
"""
|
|
||||||
kwargs = []
|
|
||||||
# has parameters
|
|
||||||
function = child.name
|
|
||||||
if hasattr(child, 'param'):
|
|
||||||
for param in child.param:
|
|
||||||
value = self.populate_param(function, param)
|
|
||||||
if not hasattr(param, 'name'):
|
|
||||||
args.append(str(value))
|
|
||||||
else:
|
|
||||||
kwargs.append(f"'{param.name}': " + value)
|
|
||||||
ret = f'Calculation(func.{function}, Params((' + ', '.join(args) + ')'
|
|
||||||
if kwargs:
|
|
||||||
ret += ', kwargs={' + ', '.join(kwargs) + '}'
|
|
||||||
ret += ')'
|
|
||||||
if hasattr(child, 'warnings_only'):
|
|
||||||
ret += f', warnings_only={child.warnings_only}'
|
|
||||||
return ret + ')'
|
|
||||||
|
|
||||||
def populate_param(self,
|
|
||||||
function: str,
|
|
||||||
param,
|
|
||||||
):
|
|
||||||
"""Populate variable parameters
|
|
||||||
"""
|
|
||||||
if param.type == 'string':
|
|
||||||
value = param.text
|
|
||||||
if value is not None:
|
|
||||||
value = self.convert_str(value)
|
|
||||||
return f"ParamValue({value})"
|
|
||||||
if param.type in ['number', 'boolean']:
|
|
||||||
return f'ParamValue({param.text})'
|
|
||||||
if param.type == 'nil':
|
|
||||||
return 'ParamValue(None)'
|
|
||||||
if param.type == 'variable':
|
|
||||||
return self.build_param(param, function)
|
|
||||||
if param.type == 'information':
|
|
||||||
return f'ParamInformation("{param.text}", None)'
|
|
||||||
if param.type == 'suffix':
|
|
||||||
return 'ParamSuffix()'
|
|
||||||
raise Exception(f'unknown type {param.type}') # pragma: no cover
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def build_param(param,
|
|
||||||
function: str, # pylint: disable=W0613
|
|
||||||
) -> str:
|
|
||||||
"""build variable parameters
|
|
||||||
"""
|
|
||||||
option_name = param.text.reflector_object.get()
|
|
||||||
params = [f'{option_name}']
|
|
||||||
if hasattr(param, 'suffix'):
|
|
||||||
param_type = 'ParamDynOption'
|
|
||||||
params.extend([f"'{param.suffix}'", f'{param.family.reflector_name}'])
|
|
||||||
else:
|
|
||||||
param_type = 'ParamOption'
|
|
||||||
if not param.propertyerror:
|
|
||||||
params.append('notraisepropertyerror=True')
|
|
||||||
return "{}({})".format(param_type, ', '.join(params))
|
|
||||||
|
|
||||||
|
|
||||||
class Family(Common):
|
|
||||||
"""Manage family
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
elt,
|
|
||||||
text,
|
|
||||||
objectspace,
|
|
||||||
):
|
|
||||||
super().__init__(elt, text, objectspace)
|
|
||||||
if hasattr(self.elt, 'suffixes'):
|
|
||||||
self.object_type = 'ConvertDynOptionDescription'
|
|
||||||
elif isinstance(self.elt, self.objectspace.leadership):
|
|
||||||
self.object_type = 'Leadership'
|
|
||||||
else:
|
|
||||||
self.object_type = 'OptionDescription'
|
|
||||||
self.children = []
|
|
||||||
|
|
||||||
def add(self, child):
|
|
||||||
"""Add a child
|
|
||||||
"""
|
|
||||||
self.children.append(child)
|
|
||||||
|
|
||||||
def _populate_attrib(self,
|
|
||||||
keys: list,
|
|
||||||
) -> None:
|
|
||||||
if hasattr(self.elt, 'suffixes'):
|
|
||||||
dyn = self.elt.suffixes.reflector_object.get()
|
|
||||||
keys['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}))))"
|
|
||||||
keys['children'] = '[' + ', '.join([child.get() for child in self.children]) + ']'
|
|
@ -1,50 +1,23 @@
|
|||||||
"""Rougail's tools
|
|
||||||
|
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
"""
|
||||||
from typing import List
|
utilitaires créole
|
||||||
from unicodedata import normalize, combining
|
"""
|
||||||
|
import unicodedata
|
||||||
from importlib.machinery import SourceFileLoader
|
from .i18n import _
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_family(family_name: str) -> str:
|
def normalize_family(family_name: str,
|
||||||
|
check_name: bool=True,
|
||||||
|
allow_dot: bool=False) -> str:
|
||||||
"""replace space, accent, uppercase, ... by valid character
|
"""replace space, accent, uppercase, ... by valid character
|
||||||
"""
|
"""
|
||||||
family_name = family_name.replace('-', '_').replace(' ', '_').replace('.', '_')
|
family_name = family_name.replace('-', '_')
|
||||||
nfkd_form = normalize('NFKD', family_name)
|
if not allow_dot:
|
||||||
family_name = ''.join([c for c in nfkd_form if not combining(c)])
|
family_name = family_name.replace('.', '_')
|
||||||
return family_name.lower()
|
family_name = family_name.replace(' ', '_')
|
||||||
|
nfkd_form = unicodedata.normalize('NFKD', family_name)
|
||||||
|
family_name = ''.join([c for c in nfkd_form if not unicodedata.combining(c)])
|
||||||
|
family_name = family_name.lower()
|
||||||
|
if check_name and family_name == 'containers':
|
||||||
|
raise ValueError(_(f'"{family_name}" is a forbidden family name'))
|
||||||
|
return family_name
|
||||||
|
|
||||||
|
|
||||||
def load_modules(eosfunc_file) -> List[str]:
|
|
||||||
"""list all functions in eosfunc
|
|
||||||
"""
|
|
||||||
loader = SourceFileLoader('eosfunc', eosfunc_file)
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
eosfunc = module_from_spec(spec)
|
|
||||||
loader.exec_module(eosfunc)
|
|
||||||
return eosfunc
|
|
||||||
|
161
src/rougail/xml_compare.py
Normal file
161
src/rougail/xml_compare.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
try:
|
||||||
|
import doctest
|
||||||
|
doctest.OutputChecker
|
||||||
|
except (AttributeError, ImportError): # Python < 2.4
|
||||||
|
import util.doctest24 as doctest
|
||||||
|
try:
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
except ImportError:
|
||||||
|
import elementtree.ElementTree as ET
|
||||||
|
from xml.parsers.expat import ExpatError as XMLParseError
|
||||||
|
|
||||||
|
RealOutputChecker = doctest.OutputChecker
|
||||||
|
|
||||||
|
|
||||||
|
def debug(*msg):
|
||||||
|
import sys
|
||||||
|
print >> sys.stderr, ' '.join(map(str, msg))
|
||||||
|
|
||||||
|
|
||||||
|
class HTMLOutputChecker(RealOutputChecker):
|
||||||
|
|
||||||
|
def check_output(self, want, got, optionflags):
|
||||||
|
normal = RealOutputChecker.check_output(self, want, got, optionflags)
|
||||||
|
if normal or not got:
|
||||||
|
return normal
|
||||||
|
try:
|
||||||
|
want_xml = make_xml(want)
|
||||||
|
except XMLParseError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
got_xml = make_xml(got)
|
||||||
|
except XMLParseError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if xml_compare(want_xml, got_xml):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def output_difference(self, example, got, optionflags):
|
||||||
|
actual = RealOutputChecker.output_difference(
|
||||||
|
self, example, got, optionflags)
|
||||||
|
want_xml = got_xml = None
|
||||||
|
try:
|
||||||
|
want_xml = make_xml(example.want)
|
||||||
|
want_norm = make_string(want_xml)
|
||||||
|
except XMLParseError as e:
|
||||||
|
if example.want.startswith('<'):
|
||||||
|
want_norm = '(bad XML: %s)' % e
|
||||||
|
# '<xml>%s</xml>' % example.want
|
||||||
|
else:
|
||||||
|
return actual
|
||||||
|
try:
|
||||||
|
got_xml = make_xml(got)
|
||||||
|
got_norm = make_string(got_xml)
|
||||||
|
except XMLParseError as e:
|
||||||
|
if example.want.startswith('<'):
|
||||||
|
got_norm = '(bad XML: %s)' % e
|
||||||
|
else:
|
||||||
|
return actual
|
||||||
|
s = '%s\nXML Wanted: %s\nXML Got : %s\n' % (
|
||||||
|
actual, want_norm, got_norm)
|
||||||
|
if got_xml and want_xml:
|
||||||
|
result = []
|
||||||
|
xml_compare(want_xml, got_xml, result.append)
|
||||||
|
s += 'Difference report:\n%s\n' % '\n'.join(result)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def xml_sort(children):
|
||||||
|
tcl1 = {}
|
||||||
|
#idx = 0
|
||||||
|
|
||||||
|
for child in children:
|
||||||
|
if 'name' in child.attrib:
|
||||||
|
key = child.attrib['name']
|
||||||
|
else:
|
||||||
|
key = child.tag
|
||||||
|
if key not in tcl1:
|
||||||
|
tcl1[key] = []
|
||||||
|
tcl1[key].append(child)
|
||||||
|
cl1_keys = list(tcl1.keys())
|
||||||
|
cl1_keys.sort()
|
||||||
|
cl1 = []
|
||||||
|
for key in cl1_keys:
|
||||||
|
cl1.extend(tcl1[key])
|
||||||
|
return cl1
|
||||||
|
|
||||||
|
def xml_compare(x1, x2):
|
||||||
|
if x1.tag != x2.tag:
|
||||||
|
print ('Tags do not match: %s and %s' % (x1.tag, x2.tag))
|
||||||
|
return False
|
||||||
|
for name, value in x1.attrib.items():
|
||||||
|
if x2.attrib.get(name) != value:
|
||||||
|
print ('Attributes do not match: %s=%r, %s=%r'
|
||||||
|
% (name, value, name, x2.attrib.get(name)))
|
||||||
|
return False
|
||||||
|
for name in x2.attrib:
|
||||||
|
if name not in x1.attrib:
|
||||||
|
print ('x2 has an attribute x1 is missing: %s'
|
||||||
|
% name)
|
||||||
|
return False
|
||||||
|
if not text_compare(x1.text, x2.text):
|
||||||
|
print ('text: %r != %r' % (x1.text, x2.text))
|
||||||
|
return False
|
||||||
|
if not text_compare(x1.tail, x2.tail):
|
||||||
|
print ('tail: %r != %r' % (x1.tail, x2.tail))
|
||||||
|
return False
|
||||||
|
|
||||||
|
cl1 = xml_sort(x1.getchildren())
|
||||||
|
cl2 = xml_sort(x2.getchildren())
|
||||||
|
|
||||||
|
if len(cl1) != len(cl2):
|
||||||
|
cl1_tags = []
|
||||||
|
for c in cl1:
|
||||||
|
cl1_tags.append(c.tag)
|
||||||
|
cl2_tags = []
|
||||||
|
for c in cl2:
|
||||||
|
cl2_tags.append(c.tag)
|
||||||
|
print ('children length differs, %i != %i (%s != %s)'
|
||||||
|
% (len(cl1), len(cl2), cl1_tags, cl2_tags))
|
||||||
|
return False
|
||||||
|
i = 0
|
||||||
|
for c1, c2 in zip(cl1, cl2):
|
||||||
|
i += 1
|
||||||
|
if not xml_compare(c1, c2):
|
||||||
|
if 'name' in c1.attrib:
|
||||||
|
name = c1.attrib['name']
|
||||||
|
else:
|
||||||
|
name = i
|
||||||
|
print ('in tag "%s" with name "%s"'
|
||||||
|
% (c1.tag, name))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def text_compare(t1, t2):
|
||||||
|
if not t1 and not t2:
|
||||||
|
return True
|
||||||
|
if t1 == '*' or t2 == '*':
|
||||||
|
return True
|
||||||
|
return (t1 or '').strip() == (t2 or '').strip()
|
||||||
|
|
||||||
|
|
||||||
|
def make_xml(s):
|
||||||
|
return ET.XML('<xml>%s</xml>' % s)
|
||||||
|
|
||||||
|
|
||||||
|
def make_string(xml):
|
||||||
|
if isinstance(xml, (str, unicode)):
|
||||||
|
xml = make_xml(xml)
|
||||||
|
s = ET.tostring(xml)
|
||||||
|
if s == '<xml />':
|
||||||
|
return ''
|
||||||
|
assert s.startswith('<xml>') and s.endswith('</xml>'), repr(s)
|
||||||
|
return s[5:-6]
|
||||||
|
|
||||||
|
|
||||||
|
def install():
|
||||||
|
doctest.OutputChecker = HTMLOutputChecker
|
||||||
|
|
@ -1,40 +1,16 @@
|
|||||||
"""load XML file from directory
|
# coding: utf-8
|
||||||
|
from os.path import join, isfile, basename, isdir
|
||||||
Created by:
|
|
||||||
EOLE (http://eole.orion.education.fr)
|
|
||||||
Copyright (C) 2005-2018
|
|
||||||
|
|
||||||
Forked by:
|
|
||||||
Cadoles (http://www.cadoles.com)
|
|
||||||
Copyright (C) 2019-2021
|
|
||||||
|
|
||||||
distribued with GPL-2 or later license
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
from typing import List
|
|
||||||
from os.path import join, isfile
|
|
||||||
from os import listdir
|
from os import listdir
|
||||||
|
#from io import BytesIO
|
||||||
|
|
||||||
from lxml.etree import DTD, parse, XMLSyntaxError # pylint: disable=E0611
|
from lxml.etree import DTD, parse, tostring # , XMLParser
|
||||||
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .error import DictConsistencyError
|
from .error import CreoleDictConsistencyError
|
||||||
|
|
||||||
|
HIGH_COMPATIBILITY = True
|
||||||
|
|
||||||
class XMLReflector:
|
class XMLReflector(object):
|
||||||
"""Helper class for loading the Creole XML file,
|
"""Helper class for loading the Creole XML file,
|
||||||
parsing it, validating against the Creole DTD,
|
parsing it, validating against the Creole DTD,
|
||||||
writing the xml result on the disk
|
writing the xml result on the disk
|
||||||
@ -59,25 +35,62 @@ class XMLReflector:
|
|||||||
|
|
||||||
:returns: the root element tree object
|
:returns: the root element tree object
|
||||||
"""
|
"""
|
||||||
try:
|
# FIXME zephir2
|
||||||
document = parse(xmlfile)
|
# document = parse(BytesIO(xmlfile), XMLParser(remove_blank_text=True))
|
||||||
except XMLSyntaxError as err:
|
document = parse(xmlfile)
|
||||||
raise DictConsistencyError(_(f'not an XML file: {err}'), 52, xmlfile) from err
|
|
||||||
if not self.dtd.validate(document):
|
if not self.dtd.validate(document):
|
||||||
dtd_error = self.dtd.error_log.filter_from_errors()[0]
|
raise CreoleDictConsistencyError(_("not a valid xml file: {}").format(xmlfile))
|
||||||
msg = _(f'not a valid XML file: {dtd_error}')
|
|
||||||
raise DictConsistencyError(msg, 43, xmlfile)
|
|
||||||
return document.getroot()
|
return document.getroot()
|
||||||
|
|
||||||
@staticmethod
|
def load_xml_from_folders(self, xmlfolders, from_zephir):
|
||||||
def load_xml_from_folders(xmlfolders: List[str]):
|
|
||||||
"""Loads all the XML files located in the xmlfolders' list
|
"""Loads all the XML files located in the xmlfolders' list
|
||||||
|
|
||||||
:param xmlfolders: list of full folder's name
|
:param xmlfolders: list of full folder's name
|
||||||
"""
|
"""
|
||||||
for xmlfolder in xmlfolders:
|
documents = []
|
||||||
filenames = [join(xmlfolder, filename) for filename in listdir(xmlfolder) if \
|
if from_zephir:
|
||||||
filename.endswith('.xml')]
|
for idx, xmlfile in enumerate(xmlfolders):
|
||||||
filenames.sort()
|
documents.append(('generate_{}'.format(idx), self.parse_xmlfile(xmlfile, from_zephir=from_zephir)))
|
||||||
for xmlfile in filenames:
|
else:
|
||||||
yield xmlfile
|
if not isinstance(xmlfolders, list):
|
||||||
|
xmlfolders = [xmlfolders]
|
||||||
|
for xmlfolder in xmlfolders:
|
||||||
|
if isinstance(xmlfolder, list) or isinstance(xmlfolder, tuple):
|
||||||
|
# directory group : collect files from each
|
||||||
|
# directory and sort them before loading
|
||||||
|
group_files = []
|
||||||
|
for idx, subdir in enumerate(xmlfolder):
|
||||||
|
if isdir(subdir):
|
||||||
|
for filename in listdir(subdir):
|
||||||
|
group_files.append((filename, idx, subdir))
|
||||||
|
else:
|
||||||
|
group_files.append(basename(subdir), idx, dirname(subdir))
|
||||||
|
def sort_group(file1, file2):
|
||||||
|
if file1[0] == file2[0]:
|
||||||
|
# sort by initial xmlfolder order if same name
|
||||||
|
return file1[1].__cmp__(file2[1])
|
||||||
|
# sort by filename
|
||||||
|
elif file1[0] > file2[0]:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
group_files.sort(sort_group)
|
||||||
|
filenames = [join(f[2], f[0]) for f in group_files]
|
||||||
|
elif isdir(xmlfolder):
|
||||||
|
filenames = []
|
||||||
|
for filename in listdir(xmlfolder):
|
||||||
|
filenames.append(join(xmlfolder, filename))
|
||||||
|
filenames.sort()
|
||||||
|
else:
|
||||||
|
filenames = [xmlfolder]
|
||||||
|
for xmlfile in filenames:
|
||||||
|
if xmlfile.endswith('.xml'):
|
||||||
|
#xmlfile_path = join(xmlfolder, xmlfile)
|
||||||
|
documents.append((xmlfile, self.parse_xmlfile(xmlfile)))
|
||||||
|
return documents
|
||||||
|
|
||||||
|
def save_xmlfile(self, xmlfilename, xml): # pylint: disable=R0201
|
||||||
|
"""Write a bunch of XML on the disk
|
||||||
|
"""
|
||||||
|
with open(xmlfilename, 'w') as xmlfh:
|
||||||
|
xmlfh.write(tostring(xml, pretty_print=True, encoding="UTF-8", xml_declaration=True).decode('utf8'))
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_2 = OptionDescription(name="tata", doc="tata", children=[])
|
|
||||||
option_2.impl_set_information('manage', True)
|
|
||||||
option_1 = OptionDescription(name="services", doc="services", children=[option_2], properties=frozenset({"hidden"}))
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_freeze="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
<variable name="module_instancie" type="string" description="No change">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non", "rougail.general.module_instancie": "non"}
|
|
@ -1,19 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_4 = StrOption(name="module_instancie", doc="No change", default="non", properties=frozenset({"mandatory", "normal"}))
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"auto_freeze", "basic", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_4, todict=True), 'expected': ParamValue("oui"), 'reverse_condition': ParamValue(True)}))}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"basic"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_freeze="True" mode="expert">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
<variable name="module_instancie" type="string" description="No change">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non", "rougail.general.module_instancie": "non"}
|
|
@ -1,19 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_4 = StrOption(name="module_instancie", doc="No change", default="non", properties=frozenset({"mandatory", "normal"}))
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"auto_freeze", "expert", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_4, todict=True), 'expected': ParamValue("oui"), 'reverse_condition': ParamValue(True)}))}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"normal"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_save="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non"}
|
|
@ -1,18 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"basic", "force_store_value", "mandatory"}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"basic"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_save="True" mode="expert">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non"}
|
|
@ -1,18 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"expert", "force_store_value", "mandatory"}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"expert"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,13 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<!-- this is a comment -->
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non"}
|
|
@ -1,18 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"normal"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
<variable name="without_type">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non", "rougail.general.without_type": "non"}
|
|
@ -1,19 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
|
|
||||||
option_4 = StrOption(name="without_type", doc="without_type", default="non", properties=frozenset({"mandatory", "normal"}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"normal"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non"}
|
|
@ -1,18 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"normal"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="général">
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" hidden="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1 +0,0 @@
|
|||||||
{"rougail.general.mode_conteneur_actif": "non", "rougail.general.mode_conteneur_actif1": "non"}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name='général'>
|
|
||||||
<variable name='mode_conteneur_actif1' type='string' description="No change" hidden="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
@ -1,19 +0,0 @@
|
|||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
|
||||||
loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
|
|
||||||
spec = spec_from_loader(loader.name, loader)
|
|
||||||
func = module_from_spec(spec)
|
|
||||||
loader.exec_module(func)
|
|
||||||
for key, value in dict(locals()).items():
|
|
||||||
if key != ['SourceFileLoader', 'func']:
|
|
||||||
setattr(func, key, value)
|
|
||||||
try:
|
|
||||||
from tiramisu3 import *
|
|
||||||
except:
|
|
||||||
from tiramisu import *
|
|
||||||
from rougail.tiramisu import ConvertDynOptionDescription
|
|
||||||
option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
|
|
||||||
option_4 = StrOption(name="mode_conteneur_actif1", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
|
|
||||||
option_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"normal"}))
|
|
||||||
option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
|
|
||||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1])
|
|
@ -1,21 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<rougail>
|
|
||||||
<variables>
|
|
||||||
<family name="general">
|
|
||||||
<variable name="module_instancie" type="string" description="No change">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
<variable name="mode_conteneur_actif" type="string" description="No change" auto_freeze="True" hidden="True">
|
|
||||||
<value>non</value>
|
|
||||||
</variable>
|
|
||||||
</family>
|
|
||||||
</variables>
|
|
||||||
<constraints>
|
|
||||||
<fill name="calc_val">
|
|
||||||
<param>oui</param>
|
|
||||||
<target>mode_conteneur_actif</target>
|
|
||||||
</fill>
|
|
||||||
</constraints>
|
|
||||||
</rougail>
|
|
||||||
<!-- vim: ts=4 sw=4 expandtab
|
|
||||||
-->
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user