|
1228 | 1228 | Of course, we just approximate the square root through several steps, so the value of the fraction might not actually be~2. |
1229 | 1229 | However, when rendered to 100~digits of precision, it should return~\pythonil{"2"}. |
1230 | 1230 |
|
1231 | | -Finally, we want to compute the \pgls{goldenRatio}~\numberGoldenRatio~\cite{CEOEB2024GR,EHF2008EEOGTGOJLH11FEEEELIEILHIATBG11EAPWMETBFR,S2024DEOGRPOT}, which equals~$\frac{1+\sqrt{5}}{2}$. |
| 1231 | +Finally, we want to compute the golden ratio~\numberGoldenRatio~\cite{CEOEB2024GR,EHF2008EEOGTGOJLH11FEEEELIEILHIATBG11EAPWMETBFR,S2024DEOGRPOT}, which equals~$\frac{1+\sqrt{5}}{2}$. |
1232 | 1232 | We can write this as \pythonil{ONE_HALF * (ONE + sqrt(Fraction(5, 1)))}. |
1233 | 1233 | In the \pgls{doctest}, we want to see whether our results are correct to 420~fractional digits, which we take from~\cite{F1996TGR1T2P}. |
1234 | 1234 |
|
|
1261 | 1261 | These methods are \pythonilIdx{\_\_enter\_\_} and \pythonilIdx{\_\_exit\_\_}. |
1262 | 1262 | Since \pythonilIdx{with}~blocks are nice syntactical sugar of the \python\ language, we will here play around with them a little bit. |
1263 | 1263 |
|
1264 | | -As example, let us create a simple API that allows us to write output in a subset of the \glsreset{xml}\pgls{xml} format~\cite{BPSMM2008EMLX1FE,K2019ITXJY,CH2013XFCAMLTMC}. |
1265 | | -\pgls{xml} is a format for data interchange which was predominant in distributed systems the~2000s. |
1266 | | -After that, it began to fade out in favor~\cite{A2020XLATDOEOL} of \pgls{json}~\cite{E2017SE4TJDIS,RFC8259} and \pgls{yaml}~\cite{DNMAASBE2021YAMLYV1,K2019ITXJY,CGTYB2022YFFDCAIE}. |
1267 | | -It is still very relevant today, for example, as foundation of several document formats such as those used in LibreOffice~\cite{DF2024LTDF,GL2012LTSOOSSCBAFACSOL} and Microsoft~Word~\cite{MS2024MW,DR2019STFAWAUMW}, or as the basis for the SVG graphics format~\cite{DDGLMSWFJJ2011SVGSSE}. |
1268 | | -If you are a bit familiar with web design, then you will find that \pgls{xml} looks a bit like HTML~\cite{HBFLDNOP2014HAVAAAFHAX}.% |
| 1264 | +As example, let us create a simple API that allows us to write output in a subset of the \glsreset{XML}\pgls{XML} format~\cite{BPSMM2008EMLX1FE,K2019ITXJY,CH2013XFCAMLTMC}. |
| 1265 | +\pgls{XML} is a format for data interchange which was predominant in distributed systems the~2000s. |
| 1266 | +After that, it began to fade out in favor~\cite{A2020XLATDOEOL} of \pgls{JSON}~\cite{E2017SE4TJDIS,RFC8259} and \pgls{YAML}~\cite{DNMAASBE2021YAMLYV1,K2019ITXJY,CGTYB2022YFFDCAIE}. |
| 1267 | +It is still very relevant today, for example, as foundation of several document formats such as those used in \libreoffice~\cite{DF2024LTDF,GL2012LTSOOSSCBAFACSOL} and \microsoftWord~\cite{MS2024MW,DR2019STFAWAUMW}, or as the basis for the \pgls{SVG} format~\cite{DDGLMSWFJJ2011SVGSSE}. |
| 1268 | +If you are a bit familiar with web design, then you will find that \pgls{XML} looks a bit like \pgls{HTML}~\cite{HBFLDNOP2014HAVAAAFHAX}.% |
1269 | 1269 | % |
1270 | 1270 | \begin{sloppypar}% |
1271 | | -As shown below, an \pgls{xml}~documents begins with the \pgls{xml} version declaration~\xmlil{<?xml version="1.0"?>}. |
1272 | | -Then, it includes \pgls{xml}~elements that can be arbitrarily nested. |
| 1271 | +As shown below, an \pgls{XML}~documents begins with the \pgls{XML} version declaration~\xmlil{<?xml version="1.0"?>}. |
| 1272 | +Then, it includes \pgls{XML}~elements that can be arbitrarily nested. |
1273 | 1273 | Each elements has a name and begins with an opening and closing string, looking somewhat like~\xmlil{<name>...</name>}. |
1274 | 1274 | Between the opening and closing string, text and other elements can be included. |
1275 | 1275 | An element can have attributes stored in the opening string, which looks like~\xmlil{<name key='value'... >...}.% |
|
1291 | 1291 | \end{minipage}% |
1292 | 1292 | \end{center}% |
1293 | 1293 | % |
1294 | | -Here we have a nicely formatted \pgls{xml} text about this course. |
| 1294 | +Here we have a nicely formatted \pgls{XML} text about this course. |
1295 | 1295 | The \xmlil{<class>} element has two attributes, \xmlil{title} and \xmlil{year}. |
1296 | 1296 | It contains other elements, such as \xmlil{<description>} with a brief description of the class. |
1297 | 1297 | Then follows the element \xmlil{<teacher>} with an attribute storing my given name as well as brief text. |
1298 | 1298 | Finally, there is an element \xmlil{<students>}, which, in turn, holds two elements of type \xmlil{<student>}. |
1299 | 1299 | Each of them hold the student's given name as attribute \xmlil{name}. |
1300 | 1300 | The second \xmlil{<student>} element includes some additional text. |
1301 | | -In this next, you notice that two characters have been \emph{escaped}: since \textil{<} and \textil{>} are used to mark the beginning and ending of the start and end element strings, they should not occur inside the normal text, as this may confuse the \pgls{xml} parsers. |
| 1301 | +In this next, you notice that two characters have been \emph{escaped}: since \textil{<} and \textil{>} are used to mark the beginning and ending of the start and end element strings, they should not occur inside the normal text, as this may confuse the \pgls{XML} parsers. |
1302 | 1302 | Therefore, they are escaped as entities~\xmlil{&} and~\xmlil{>}. |
1303 | 1303 |
|
1304 | 1304 | \gitPython{\programmingWithPythonCodeRepo}{dunder/xml_context.py}{--args format --labels part_1}{dunder:xml_context:part_1}{% |
1305 | | -Part~1 of our very simply context manager-based \pgls{xml} output API.}% |
| 1305 | +Part~1 of our very simply context manager-based \pgls{XML} output API.}% |
1306 | 1306 | % |
1307 | 1307 | \gitPython{\programmingWithPythonCodeRepo}{dunder/xml_context.py}{--args format --labels part_2}{dunder:xml_context:part_2}{% |
1308 | | -Part~2 of our very simply context manager-based \pgls{xml} output API.}% |
| 1308 | +Part~2 of our very simply context manager-based \pgls{XML} output API.}% |
1309 | 1309 |
|
1310 | | -Would it not be nice to have a simple \pgls{API} that allows us to produce valid~\pgls{xml} and that takes care of the escaping of special characters? |
| 1310 | +Would it not be nice to have a simple \pgls{API} that allows us to produce valid~\pgls{XML} and that takes care of the escaping of special characters? |
1311 | 1311 | While countless such tools already exist {\dots} let us make our own. |
1312 | 1312 | For this, we realize: |
1313 | | -First, every \pgls{xml} element that is opened must also be closed. |
1314 | | -Second, \pgls{xml} elements can be nested arbitrarily. |
| 1313 | +First, every \pgls{XML} element that is opened must also be closed. |
| 1314 | +Second, \pgls{XML} elements can be nested arbitrarily. |
1315 | 1315 | This kind of looks like an application of the \pythonilIdx{with} statement. |
1316 | 1316 |
|
1317 | 1317 | We now make our own context manager. |
1318 | 1318 | When the \pythonilIdx{with} statement begins, our context manager will write the element start text. |
1319 | 1319 | Inside the body of the \pythonilIdx{with}~statement, we will allow to write the element text or to open sub-elements. |
1320 | | -At the end of the \pythonilIdx{with}~statement, the \pgls{xml} element closing text should be written. |
| 1320 | +At the end of the \pythonilIdx{with}~statement, the \pgls{XML} element closing text should be written. |
1321 | 1321 | Furthermore, we want to be able to direct the output of our \pgls{API} to any destination where strings can be written to, say, \pythonil{print}, to \pythonils{list}, or to files. |
1322 | 1322 | As you will see in \cref{lst:dunder:xml_context:part_1,lst:dunder:xml_context:part_2}, we can implement all of that in a fairly small module. |
1323 | 1323 |
|
1324 | | -In \cref{lst:dunder:xml_context:part_1}, we begin by defining an internal constant \pythonil{_ESC}, which we use for \pgls{xml} escaping special characters in strings with \pgls{xml}~entities. |
| 1324 | +In \cref{lst:dunder:xml_context:part_1}, we begin by defining an internal constant \pythonil{_ESC}, which we use for \pgls{XML} escaping special characters in strings with \pgls{XML}~entities. |
1325 | 1325 | For this purpose, we use the functions \pythonilIdx{maketrans}\pythonIdx{str!maketrans} and \pythonilIdx{translate}\pythonIdx{str!translate} of the class~\pythonilIdx{str}. |
1326 | 1326 | The former accepts a dictionary where the keys are single characters and the corresponding values are strings with which these characters should be replaced. |
1327 | 1327 | It creates some \python-internal datastructure which then can be passed to \pythonilIdx{translate}\pythonIdx{str!translate} to perform the replacement. |
1328 | 1328 | For example, we could do~\pythonil{x = str.maketrans(\{"A": "XYZ"\})}. |
1329 | 1329 | Then, \pythonil{"ABCBA".translate(x)} would yield~\pythonil{"XYZBCBXYZ"}. |
1330 | | -This is very useful to painlessly implement rudimentary support to escape special characters based on the \pgls{xml}~standard~\cite{BPSMM2008EMLX1FE}. |
| 1330 | +This is very useful to painlessly implement rudimentary support to escape special characters based on the \pgls{XML}~standard~\cite{BPSMM2008EMLX1FE}. |
1331 | 1331 |
|
1332 | 1332 | Then we define the \pythonil{class Element}. |
1333 | 1333 | The initializer~\dunder{init} of this class has four important parameters. |
|
1337 | 1337 | \pythonil{attrs}~contains the attributes of the element. |
1338 | 1338 | It is either \pythonil{None} if there are no attributes (which is the default). |
1339 | 1339 | Otherwise, it is a \pythonilIdx{dict} mapping string keys to arbitrary values. |
1340 | | -Finally, \pythonil{is_root} is a Boolean value that is \pythonil{True} if this element is the single root element of the \pgls{xml}~document that we want to write, and \pythonil{False} if it is some nested element. |
1341 | | -This is needed, because before writing the root element, the \pgls{xml} version declaration~\xmlil{<?xml version="1.0"?>} must be written. |
1342 | | -This must happen only once and only at the beginning of an \pgls{xml}~document. |
| 1340 | +Finally, \pythonil{is_root} is a Boolean value that is \pythonil{True} if this element is the single root element of the \pgls{XML}~document that we want to write, and \pythonil{False} if it is some nested element. |
| 1341 | +This is needed, because before writing the root element, the \pgls{XML} version declaration~\xmlil{<?xml version="1.0"?>} must be written. |
| 1342 | +This must happen only once and only at the beginning of an \pgls{XML}~document. |
1343 | 1343 |
|
1344 | 1344 | In the initializer, we store the \pythonil{dest} argument in an attribute that is both private~(signified by the two leading underscores) and immutable~(signified by the \pgls{typeHint}~\pythonilIdx{Final}). |
1345 | 1345 | We then construct the element start string by filling a list~\pythonil{head} and store its concatenated elements in the attribute~\pythonil{__head}. |
1346 | | -Only if our element is a root element, the \pgls{xml} version declaration is included in the list~\pythonil{head}. |
| 1346 | +Only if our element is a root element, the \pgls{XML} version declaration is included in the list~\pythonil{head}. |
1347 | 1347 | The actual element start string begins by the less-than symbol and the element name~(\xmlil{<name}). |
1348 | 1348 | Then, if \pythonil{attrs} is neither \pythonil{None} nor empty, we want to add the attributes. |
1349 | 1349 | Interestingly, this condition can be expressed by a simple~\pythonil{if attrs:}. |
|
1405 | 1405 | The former is very easy: |
1406 | 1406 | We define a method~\pythonil{text} taking a string~\pythonil{txt} as input parameter. |
1407 | 1407 | All we have to do in the body of this method is~\pythonil{self.__dest(txt.translate(_ESC))}. |
1408 | | -This escapes any dangerous \pgls{xml} special characters in the string and passes the result on to the output destination. |
| 1408 | +This escapes any dangerous \pgls{XML} special characters in the string and passes the result on to the output destination. |
1409 | 1409 |
|
1410 | 1410 | The method~\pythonil{element} is used to branch off new sub-elements. |
1411 | 1411 | It basically would take the same parameters as the initializer~\pythonil{\_\_init\_\_}. |
1412 | 1412 | However, the new element must use the same output destination and it cannot be a root element (because it is a sub-element). |
1413 | 1413 | Therefore, we only need to pass the parameters~\pythonil{name} and~\pythonil{attrs} to a new instance of~\pythonil{Element}, whereas we hand over \pythonil{self.__dest} as destination and \pythonil{False} as \pythonil{is_root}. |
1414 | | -With this, our simple \pgls{API} for a subset of the \pgls{xml} standard~\cite{BPSMM2008EMLX1FE} is already completed.% |
| 1414 | +With this, our simple \pgls{API} for a subset of the \pgls{XML} standard~\cite{BPSMM2008EMLX1FE} is already completed.% |
1415 | 1415 | % |
1416 | 1416 | \gitPythonAndOutputFormat{xml_style}% |
1417 | 1417 | \gitPythonAndOutput{\programmingWithPythonCodeRepo}{dunder}{xml_user_print.py}{--args format}{dunder:xml_user_print}{% |
1418 | | -An example of using our simple context manager-based \pgls{xml} output API from \cref{lst:dunder:xml_context:part_1,lst:dunder:xml_context:part_2}, where the output is printed to the \pgls{stdout}.}% |
| 1418 | +An example of using our simple context manager-based \pgls{XML} output API from \cref{lst:dunder:xml_context:part_1,lst:dunder:xml_context:part_2}, where the output is printed to the \pgls{stdout}.}% |
1419 | 1419 | % |
1420 | 1420 | \gitPythonAndOutputFormat{xml_style}% |
1421 | 1421 | \gitPythonAndOutput{\programmingWithPythonCodeRepo}{dunder}{xml_user_file.py}{--args format}{dunder:xml_user_file}{% |
1422 | | -An example of using our simple context manager-based \pgls{xml} output API from \cref{lst:dunder:xml_context:part_1,lst:dunder:xml_context:part_2}, where the output is written to a text file\pythonIdx{write}\pythonIdx{IO!write}\pythonIdx{open}\pythonIdx{remove}\pythonIdx{os!remove}.}% |
| 1422 | +An example of using our simple context manager-based \pgls{XML} output API from \cref{lst:dunder:xml_context:part_1,lst:dunder:xml_context:part_2}, where the output is written to a text file\pythonIdx{write}\pythonIdx{IO!write}\pythonIdx{open}\pythonIdx{remove}\pythonIdx{os!remove}.}% |
1423 | 1423 |
|
1424 | | -We now use this \pgls{API} in \cref{lst:dunder:xml_user_print,lst:dunder:xml_user_file} to basically reproduce the small \pgls{xml}~snippet that I showed you before. |
| 1424 | +We now use this \pgls{API} in \cref{lst:dunder:xml_user_print,lst:dunder:xml_user_file} to basically reproduce the small \pgls{XML}~snippet that I showed you before. |
1425 | 1425 | In the former example, we use \pythonil{print} as destination function. |
1426 | 1426 | This means that any string that our \pgls{API} passes to its internal \pythonil{__dest}~attribute will immediately be written as a single line to the~\pgls{stdout}. |
1427 | 1427 |
|
|
1433 | 1433 | Notice that our \pgls{API} writes the element start and end strings to the output without requiring us to do anything. |
1434 | 1434 | It also escapes all special characters for us. |
1435 | 1435 | The other elements are created in the same convenient fashion. |
1436 | | -Our \python\ code basically mirrors the \pgls{xml} structure. |
| 1436 | +Our \python\ code basically mirrors the \pgls{XML} structure. |
1437 | 1437 | The output of the program in \cref{exec:dunder:xml_user_print} looks very much like our example, with a slightly different indentation and line breaking. |
1438 | | -But this is permitted and acceptable under the \pgls{xml} standard~\cite{BPSMM2008EMLX1FE}. |
| 1438 | +But this is permitted and acceptable under the \pgls{XML} standard~\cite{BPSMM2008EMLX1FE}. |
1439 | 1439 |
|
1440 | 1440 | Instead of writing to the \pgls{stdout}, we can also recall our very first example for the \pythonilIdx{with} statement back in \cref{sec:withAndContextManagers}: |
1441 | 1441 | writing to and reading from a file. |
|
1447 | 1447 | Files are actually \pythonilsIdx{Iterator} of line strings! |
1448 | 1448 | All the file contents get written to the \pgls{stdout}. |
1449 | 1449 | Since \pythonilIdx{write} does not append newline characters, this format is much more compact, as can be seen in~\cref{exec:dunder:xml_user_file}. |
1450 | | -But it is perfectly valid~\pgls{xml}. |
| 1450 | +But it is perfectly valid~\pgls{XML}. |
1451 | 1451 |
|
1452 | 1452 | We now have learned how the functionality of the \pythonilIdx{with}~statement is implemented internally. |
1453 | | -And we used it to hammer together a very compact and yet functional \pgls{API} for a subset of the \pgls{xml} standard~\cite{BPSMM2008EMLX1FE}. |
| 1453 | +And we used it to hammer together a very compact and yet functional \pgls{API} for a subset of the \pgls{XML} standard~\cite{BPSMM2008EMLX1FE}. |
1454 | 1454 | Obviously, we do not implement the complete standard, which is much more complicated. |
1455 | | -And you should never use our class if you really wanted to produce \pgls{xml} in a productive code. |
| 1455 | +And you should never use our class if you really wanted to produce \pgls{XML} in a productive code. |
1456 | 1456 | We also omitted type and sanity checks -- for example, we should forbid element names that are empty or contain special characters like~\textil{<}. |
1457 | 1457 | A real implementation would be more conservative~(and too long to serve as a good example in this book). |
1458 | | -Still, on one hand, our \pgls{xml} is valid. |
| 1458 | +Still, on one hand, our \pgls{XML} is valid. |
1459 | 1459 | On the other hand, you \emph{could} use extend and improve this class to have all the functionality that you need, if you wanted to. |
1460 | 1460 | So this is actually another example that, at this stage and with what you have learned, you can already do real things.% |
1461 | 1461 | % |
|
0 commit comments