Creating new block types
Binding to a typename
A block type can be defined just by hooking the generator function to the type.
Python Code
import markdown
md = markdown.Markdown(
extensions=["customblocks"],
extension_configs=dict(
generators=dict(
customblocks={
# by direct symbol reference
'mytype': mypackage.mymodule.mytype,
# or using import strings (notice the colon)
'aka_mytype': 'mypackage.mymodule:mytype',
...
}
)
),
)
md.convert(markdowncontent)
Command line config
If you are using markdown_py
command line,
you can use the -c config.yaml
option,
and, in config.yaml
, add:
Pelican
In pelican.conf
:
MARKDOWN = {
...
'extensions_configs': {
'customblocks': {
'generators': {
# by direct symbol reference
'mytype': mypackage.mymodule.mytype,
# or using import strings (notice the colon)
'aka_mytype': 'mypackage.mymodule:mytype',
}
},
},
}
MkDocs
In mkdocs.yaml
:
markdown_extensions:
- customblocks:
generators:
mytype: mypackage.mymodule:mytype,
aka_mytype: mypackage.mymodule:mytype
Packaging
If you are distributing the generator as a package,
and want it to be bound to a typename on install,
register an entry point in the markdown.customblocks.generators
group.
In setup.py
:
setup(
...
entry_points={
...
markdown.customblocks.generators': [
'mytype = mypackage.mymodule:mytype',
'aka_mytype = mypackage.mymodule:mytype',
],
},
...
}
Warning
Conflicting entrypoints from different packages are resolved randomly. Because of that, be carefull not to register names other developers have already used.
It is ok to redefine an existing block type, but let the user to pick it explicitly on config.
Parameter mapping
The signature of the generator will determine the attributes taken from the headline. Say you have a generator with the following signature:
Function parameters are filled using values parsed from head line. Unlike Python, you can interleave in the headline values with and without keys. They are resolved as follows:
- Context: The
ctx
parameter is ignored for parameter matching. See bellow on how to use it. - Explicit keys: First,
customblocks
binds keyworded values on the headline whose key matches a parameter name in the generator (Excluding parameters defined as 'positional-only') - Flag: Flags are parameters either annotated as
bool
(like example'smyflag
), or defaulting toTrue
orFalse
, (like example'syourflag
). They are filled as follows:- When a keyless value matches a flag name in the generator (
myflag
),True
is passed - When it matches the flag name prefixed with
no
(nomyflag
),False
is passed
- When a keyless value matches a flag name in the generator (
- Positional: Keyless values in the headline are assigned one-to-one by position order to the unassigned parameters, (excluding those defined as 'keyword-only').
- Varidics: If the signature contains key (
**kwds
) or positional (*args
) varidic variables, any remaining key and keyless values from the headline are assigned to them - Unmatched function parameters: If they have no default value, will be warned and assigned an empty string.
- Unmatched headline parameters: They will be warned and ignored.
For example, by fedding the followin headline to the signature above:
Will set:
param1 = "value3" # This will be matched first since uses a explicit key
yourFlag = False # Flags are set later, from noyourflag
myflag = True # From myflag value
param2 = "value1" # param1 already filled, next in signature is param2
param3 = "" # No value given, will be warned, and set to ''
param4 = "default2" # this one will get its default value, no warning given
# Also warn about unexpected `bad` key, and ignored
The context object
Regarding the ctx
parameter, it is the context.
If you don't use it, you can skip it.
But it is useful if you want to receive some context parameters like:
ctx.parent
: the parent nodectx.content
: the indented part of the block, with the indentation removedctx.parser
: the markdown parser, can be used to parse the inner content or any other markdown codectx.type
: the type of the block- If you reuse the same function for different types, this is how you discriminate them
ctx.metadata
: A dictionary with metadata from your metadata plugin.ctx.config
: A dictionary passed fromextension_configs.customblocks.config
Producing HTML
A generator can use several strategies to generate content:
- Return an html string (single root node)
- Return a
markdown.etree
Element
object - Manipulate
ctx.parent
to add the content and returnNone
In order to construct an ElementTree, we recommend using the Hyperscript utility. Resulting code will be more compact and readable and makes proper escaping when injecting values.
Generator helpers
Common code has been extracted from predefined generators. If you need this functionality you are encouraged to use them.
- Hyperscript: to generate html
- PageInfo: to extract metadata from a webpage
- Fetcher: to download resources with file based cache
Hyperscript
You can generate html with strings or using etree
; but there is a more elegant option.
Hyperscript is the idea of writing code that generates html/xml
as nested function calls that look like the actual xml structure.
This can be done by using the customblocks.utils.E
function which has this signature:
tag
is the name of the tag (pre
, div
, strong
...).
An empty string is equivalent to div
.
It can have appended several .classname
that will be added as element class.
Any keyword parameter will be taken as element attributes.
You can use the special _class
attribute to append more classes.
Notice the underline, as class
is a reserved word in Python.
children
takes the keyless parameters and they can be:
None
: then it will be ignoreddict
: it will be merged with the attributesstr
: it will be added as textetree.Element
: it will be added as child nodecustomblocks.utils.Markdown
: will append parsed markdown (see below)- Any
tuple
,list
or iterable: will add each item following previous rules
from customblocks.utils import E, Markdown
def mytype(ctx, image):
return (
E('.mytype',
dict(style="width: 30%; align: left"),
E('a', dict(href=image),
E('img', src=image),
),
Markdown(ctx.content, ctx.parser),
)
)
PageInfo
utils.pageinfo.PageInfo
is a class that retrieves
meta information from html pages by means of its properties.
Properties are computed lazily and use cache. Once you get one property for a given page, later uses will have little impact.
Any attribute you explicit on the constructor will override the ones derived from actual content.
info = PageInfo(html, url='http://site.com/path/page.html')
info.sitename # the name of the site (meta og:site_name or the domain
info.siteicon # the favicon or similar
info.siteurl # the base url of the site (not the page)
info.title # page title (from og:title meta or `<title>` content)
info.description # short description (from og:description or twitter:description)
info.image # featured image (from og:image or twitter:image, or site image)
Fetcher
A fetcher object is a wrapper around the requests
library
that uses caching to avoid downloading once and again remote resource
each time you compile the markdown file.
The first time a resource is succesfully downloaded by a fetcher the request response is stored in the provided folder in a yaml file which has the mangled url as name. Successive tries to download it just take the content of that file to construct a query.