mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 11:32:39 +02:00
update
This commit is contained in:
parent
dd02b45676
commit
9563e8731e
7 changed files with 293 additions and 11 deletions
162
content/code/2024-06-03-external-process-pytest-xdist.md
Normal file
162
content/code/2024-06-03-external-process-pytest-xdist.md
Normal file
|
@ -0,0 +1,162 @@
|
|||
---
|
||||
title: Start a process when using pytest-xdist
|
||||
tags: django, tests, pytest
|
||||
---
|
||||
|
||||
Here is how to start a process inside a test suite, using a pytest fixture. This comes
|
||||
straight from [this post](https://til.simonwillison.net/pytest/subprocess-server).
|
||||
|
||||
```python
|
||||
def run_websocket_server(server_running_file=None):
|
||||
ds_proc = subprocess.Popen(
|
||||
[
|
||||
"umap",
|
||||
"run_websocket_server",
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
time.sleep(2)
|
||||
# Ensure it started properly before yielding
|
||||
assert not ds_proc.poll(), ds_proc.stdout.read().decode("utf-8")
|
||||
yield ds_proc
|
||||
# Shut it down at the end of the pytest session
|
||||
ds_proc.terminate()
|
||||
```
|
||||
|
||||
This works well in general, but when using [pytest-xdist](https://pytest-xdist.readthedocs.io/en/stable/)
|
||||
to distribute the tests on multiple processes, the server will try to be started
|
||||
multiple times, where we want it to start only once.
|
||||
|
||||
It is possible to mark the tests you want to group together to run on
|
||||
the same process, using "load groups":
|
||||
|
||||
```python
|
||||
@pytest.mark.xdist_group(name="websockets")
|
||||
def test_something(websocket_server):
|
||||
# do something here.
|
||||
```
|
||||
|
||||
When running the tests, you will need to pass a flag to pytest:
|
||||
|
||||
```bash
|
||||
pytest --dist=loadgroup
|
||||
```
|
||||
---
|
||||
|
||||
## A messier solution using file locks
|
||||
|
||||
Here is another solution I wrote, which is more involved and… messier.
|
||||
|
||||
Still, I'm putting it here in case it helps later on. Basically, it is using
|
||||
files as inter-process communication.
|
||||
|
||||
You've been warned, you don't need to read this. If possible, use the version at
|
||||
the beginning of this post instead.
|
||||
|
||||
```python
|
||||
@pytest.fixture(scope="session")
|
||||
def websocket_server(tmp_path_factory, worker_id):
|
||||
# Because we are using xdist to paralellize the tests, the "session" scope can
|
||||
# happen more than once per test session.
|
||||
# This fixture leverages lockfiles to ensure the server is only started once.
|
||||
|
||||
if worker_id == "master":
|
||||
# This only runs when no workers are used ("pytest -n0" invocation)
|
||||
yield from run_websocket_server()
|
||||
else:
|
||||
# The first runner getting there will make the others wait on it
|
||||
# Two files are at play here. the first selects one worker to start the server
|
||||
# And the second one tells other workers the server is up and running
|
||||
temp_folder = tmp_path_factory.getbasetemp().parent
|
||||
lock_file = temp_folder / "ws_starting.lock"
|
||||
server_running_file = temp_folder / "ws_running"
|
||||
|
||||
def _wait_for_server():
|
||||
while not server_running_file.exists():
|
||||
time.sleep(0.1)
|
||||
|
||||
if not lock_file.exists():
|
||||
# Start the server
|
||||
with temporary_file(lock_file) as server_starting:
|
||||
if server_starting:
|
||||
yield from run_websocket_server(server_running_file)
|
||||
else:
|
||||
_wait_for_server()
|
||||
yield
|
||||
else:
|
||||
_wait_for_server()
|
||||
yield
|
||||
|
||||
|
||||
def run_websocket_server(server_running_file=None):
|
||||
# Find the test-settings, and put them in the current environment
|
||||
settings_path = (Path(__file__).parent.parent / "settings.py").absolute().as_posix()
|
||||
os.environ["UMAP_SETTINGS"] = settings_path
|
||||
|
||||
ds_proc = subprocess.Popen(
|
||||
[
|
||||
"umap",
|
||||
"run_websocket_server",
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
time.sleep(2)
|
||||
# Ensure it started properly before yielding
|
||||
assert not ds_proc.poll(), ds_proc.stdout.read().decode("utf-8")
|
||||
|
||||
# When the server has started, create a file to tell the other workers
|
||||
# they can go ahead.
|
||||
with temporary_file(server_running_file):
|
||||
yield ds_proc
|
||||
# Shut it down at the end of the pytest session
|
||||
ds_proc.terminate()
|
||||
|
||||
@contextmanager
|
||||
def temporary_file(path):
|
||||
"""Creates a temporary file, yield, and remove it afterwards."""
|
||||
if not path:
|
||||
yield
|
||||
else:
|
||||
try:
|
||||
with path.open("x"):
|
||||
yield True
|
||||
path.unlink()
|
||||
except FileExistsError:
|
||||
yield False
|
||||
```
|
||||
|
||||
So, what happens here?
|
||||
|
||||
- When the tests are running without processes, we just run the websocket server ;
|
||||
- When running with multiple workers, we use a lockfile, so that the first who
|
||||
gets there will block the other ones, until the server is started.
|
||||
- The signal for letter the other processes that the server started is to write
|
||||
a file.
|
||||
|
||||
---
|
||||
|
||||
## Give me back `print()` when using pytest-xdist
|
||||
|
||||
Also worth noting that pytest-xdists makes it hard to use `print` statements
|
||||
when debugging, because it captures output, even when using `pytest -s`
|
||||
|
||||
I found [this solution](https://github.com/pytest-dev/pytest-xdist/issues/354)
|
||||
to help when trying to debug:
|
||||
|
||||
```python
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def fix_print():
|
||||
"""
|
||||
pytest-xdist disables stdout capturing by default, which means that print() statements
|
||||
are not captured and displayed in the terminal.
|
||||
That's because xdist cannot support -s for technical reasons wrt the process execution mechanism
|
||||
https://github.com/pytest-dev/pytest-xdist/issues/354
|
||||
"""
|
||||
original_print = print
|
||||
with patch("builtins.print") as mock_print:
|
||||
mock_print.side_effect = lambda *args, **kwargs: original_print(*args, **{"file": sys.stderr, **kwargs})
|
||||
yield mock_print
|
||||
```
|
|
@ -4,6 +4,27 @@ save_as: dangerzone/index.html
|
|||
template: worklog-en
|
||||
---
|
||||
|
||||
## Jeudi 30 Mai 2024 (8h, 4/5)
|
||||
|
||||
- Reproducing the currently failing CI issues locally and finding out that it might be related to the way the inner image current state, as it seems to not contain the pymupdf python module.
|
||||
- 1:1 with Harris
|
||||
- Dangerzone biweekly meeting w/ erik, alex and harris.
|
||||
|
||||
## Mercredi 28 Mai 2024 (9h, 5/5)
|
||||
|
||||
- While trying to install the `rpm` files generated by a_rpygio, I finally found out that the silicon m1 machine I'm using isn't fit for the job, as it's currently hitting some bugs when running "podman in docker", with rosetta enabled on the host. I created an issue on the repository about this: https://github.com/freedomofpress/dangerzone/issues/824
|
||||
- I've switched to my linux amd64 machine, and was able to install the dangerzone dev environment there, and test that the rpms are working great. I've validated the pending pull request by a_rpygio accordingly.
|
||||
- I'm currently following-up on the python 3.9 version bump, as the produced `.deb` packages don't seem to work properly. I'm currently trying to reproduce the issue locally, with the hopes of fixing it tomorrow.
|
||||
|
||||
|
||||
## Mardi 28 Mai 2024 (6h, 5/5)
|
||||
|
||||
- Learned how Debian python packaging works, and specifics of how `stdeb` does it.
|
||||
- Reviewed PRs by AlexP about
|
||||
- A 1:1 with AlexP where we discussed both the release process for fedora and debian packages, what are the specificities on how we're doing the signing etc.
|
||||
- Took another approach for supporting the latest pyside6 version on debian + from sources.
|
||||
- Started testing the new `rpms` that were uploaded by AlexP. Been caught on the silicon architecture differences, and started making changes in the current scripts for it to work.
|
||||
|
||||
## Lundi 27 Mai 2024 (7h, 4/5)
|
||||
|
||||
- Created PGP keys for `alexis@freedom.press` and setup github to work with it.
|
||||
|
|
|
@ -4,6 +4,25 @@ save_as: umap/index.html
|
|||
template: worklog
|
||||
total_days: 90
|
||||
---
|
||||
|
||||
## Lundi 03 Juin 2024 (6h, 5/5)
|
||||
|
||||
- Trouvé une solution pour que le serveur websocket ne soit lancé que sur un seul worker lorsqu'on utilise pytest. Je me retrouvais dans une situation où le serveur websocket n'arrivait pas à se lancer de manière séparée. [Plus de détails ici](https://blog.notmyidea.org/start-a-process-when-using-pytest-xdist.html)
|
||||
- Une session avec David durant laquelle on parle des messages d'altertes qui sont affichés lorsqu'il y a conflit. Je me rends compte qu'il serait potentiellement intéressant de changer la manière dont l'algorythme de merge fonctionne, pour lui faire utiliser les `ids` qui ont été introduits sur les features. Ça nous permettrait sans doute d'être plus précis.
|
||||
|
||||
|
||||
## Vendredi 31 Mai 2024 (9h, 6/5)
|
||||
|
||||
- Rebase la PR sur la synchro, et traité les points qui étaient en attente (il y en avait quelques uns !). Entre autres:
|
||||
- Rendu les settings pour la synchro plus compréhensibles en dissociant le « front » et le « back ».
|
||||
- Ajouté la possibilité de lancer le serveur websockets avec un commande django `umap run_websocket_server`
|
||||
- Modifié l'API pour synchroniser, qui est maintenant beaucoup plus compréhensible (`this.sync.update("key", "value)`)
|
||||
- Fait un point avec Yohan et David sur leurs avancées ces dernières semaines, dans lequel on a passé en revue les derniers bouts qui restaient à discuter sur la PR.
|
||||
|
||||
J'ai très envie de merger, mais il me reste quelques petits trucs, entre autres que les tests ne passent pas pour le moment. J'ai l'impression que c'est peut-être du en partie à un rebase trop rapide, et au fait que l'outil que j'utilise pour lancer une commande en tant que fixture `pytest` ne semble pas fonctionner correctement.
|
||||
|
||||
Je me demande si je ne vais pas tout simplement le faire à la main 🤔
|
||||
|
||||
## Jeudi 16 Mai 2024 (7h, 4/5)
|
||||
|
||||
De la revue de code, des tests, de la documentation, et un fix, après m'être rendu compte que l'algorithme de merge sur le serveur dupliquait des données en essayant lui aussi de fusionner les modifications, alors qu'elles étaient déjà à jour. La résolution du problème était simplement de le mettre au courant en propageant la version de référence.
|
||||
|
|
|
@ -7,7 +7,8 @@ projects: dangerzone
|
|||
# Notes hebdo #31
|
||||
|
||||
**[Danger Zone](https://dangerzone.rocks/)** ⚠️
|
||||
*DangerZone transforme des documents potentiellement dangereux en documents sûrs.
|
||||
|
||||
*DangerZone transforme des documents potentiellement dangereux en documents sûrs.*
|
||||
|
||||
- Ma première semaine de travail pour Freedom of the Press Foundation !
|
||||
- Une rencontre avec l'équipe actuelle et la personne que je remplace.
|
||||
|
@ -26,7 +27,7 @@ projects: dangerzone
|
|||
## Des peines 😬
|
||||
|
||||
- J'ai envie de prouver que je suis à la bonne place… Confiance en toi, mon amour ⛱. J'aimerai accepter qu'il n'y a rien à prouver.
|
||||
- Je me sens stressé de bientôt devoir travailler en même temps sur uMap et Dangerzone. J'ai bien fait de prévoir du temps pour commencer, je redoute les semaines où je devrai faire les deux en même temps, ça risque de faire trop. Envie de m'écouter et de me laisser la place de
|
||||
- Je me sens stressé de bientôt devoir travailler en même temps sur uMap et Dangerzone. J'ai bien fait de prévoir du temps pour commencer, je redoute les semaines où je devrai faire les deux en même temps, ça risque de faire trop. Envie de m'écouter et de me laisser la place pour des respirations.
|
||||
- Je n'ai pas assez bougé 😬, surement la conséquence de travailler depuis la maison. J'ai bien envie d'aller faire des ballades régulièrement !
|
||||
- J'ai mal au dos et je ne m'en occupe toujours pas. J'aimerai que ce soit déjà derrière moi.
|
||||
- Je n'ai pas pris le temps de faire de la méditation alors que clairement ça aurait été utile. J'aimerai faire les bons choix cette semaine :-)
|
||||
|
@ -39,9 +40,7 @@ projects: dangerzone
|
|||
- 🎬 Vu « Chien de la casse » (de Jean Baptiste Durand, avec Raphaël Quenard), j'ai apprécié les questions qui sont soulevées par le film, autour de l'amitié, de l'acceptable et de ce qu'on peut se sentir autorisé à faire « pour le bien » des autres.
|
||||
- 🎵 Vu [Randy](https://www.youtube.com/watch?app=desktop&v=Tca42K8FKBc&feature=youtu.be) et [Roszalie](https://www.youtube.com/watch?v=ZnkKNlm6pDc) lors d'un petit festival du coin. J'aime beaucoup le flow du premier, très chouette moment, malgré le côté « je me la pète », et le second était vraiment spéctaculaire. Un bon moment.
|
||||
|
||||
### Notes
|
||||
|
||||
- [The Container Security Platform | gVisor](https://gvisor.dev/)
|
||||
## Notes
|
||||
|
||||
### Magic Numbers
|
||||
|
||||
|
|
73
content/weeknotes/32.md
Executable file
73
content/weeknotes/32.md
Executable file
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
date: 2024-06-03
|
||||
headline: notes hebdo de la semaine
|
||||
projects: dangerzone, umap
|
||||
---
|
||||
# Notes hebdo #32
|
||||
|
||||
**[Danger Zone](https://dangerzone.rocks/)** ⚠️
|
||||
|
||||
*DangerZone transforme des documents potentiellement dangereux en documents sûrs.*
|
||||
|
||||
- Seconde semaine sur le projet.
|
||||
- J'ai passé du temps à faire fonctionner l'environnement de travail, pour [me rendre compte](https://github.com/freedomofpress/dangerzone/issues/824) que ça ne fonctionnait pas forcement aussi facilement que prévu sur Silicon Mac (Podman dans Docker sur d'autres architectures, ça bloque)
|
||||
- Changement de stratégie et je ressors mon x230 du placard, sous Arch Linux. Il semble faire l'affaire. A suivre.
|
||||
- J'ai tenté de changer la version minimum de Python qui est supportée, l'occasion de découvrir comment fonctionne la *mise en paquets* du logiciel, vers debian et fedora pour le moment.
|
||||
- J'apprends à utiliser podman et docker, et je découvre leurs arcanes.
|
||||
- J'ai créé des clés PGP, et fait en sorte que [git fonctionne avec différentes identités](https://blog.notmyidea.org/multiple-identities-and-gpg-keys-for-git.html). Les clés sont [disponibles ici](https://blog.notmyidea.org/keys/).
|
||||
|
||||
**[uMap](https://umap-project.org)** 🗺️
|
||||
|
||||
*uMap est un outil libre pour faire des cartes personnalisées*
|
||||
|
||||
- Une reprise sur uMap après 10 jours sur Dangerzone.
|
||||
- Rebasé [la PR sur la synchro](https://github.com/umap-project/umap/pull/1754), et traité les points qui étaient en attente (il y en avait quelques uns !).
|
||||
- Fait un point avec Yohan et David sur leurs avancées ces dernières semaines, dans lequel on a passé en revue les derniers bouts qui restaient à discuter sur la PR.
|
||||
|
||||
Il reste encore quelques petits trucs à faire pour pouvoir merger, chouette !
|
||||
## Des joies 🤗
|
||||
|
||||
- Prendre le temps d'une séance d'étirements et de méditation le matin avant de commencer la journée. Je me sens mieux dans mon corps et ça m'aide à passer de meilleures journées.
|
||||
- Séparer mon espace pro et mon espace perso. Ça me donne de l'air.
|
||||
- Dormir mieux, dans l'ensemble, même si il reste quelques ajustements à faire !
|
||||
- Écrire, le temps d'une journée d'atelier d'écriture, hors du temps.
|
||||
- Accepter les propositions et les retours, plutôt que de me positionner contre. Après tout, chaque retour est porteur d'un changement.
|
||||
- Lister les tâches de ma journée à son commencement, et dérouler.
|
||||
|
||||
## Des peines 😬
|
||||
|
||||
- J'étais extrêmement fatigué samedi soir, une journée trop remplie, avec beaucoup de trajets. Et du coup un weekend à peine assez long pour récupérer.
|
||||
- J'ai passé beaucoup de temps en dehors de ma zone de confort, sans encore réussir à aller au bout. Je n'arrive pas encore à comprendre quand ce que je vois est lié à mon installation ou lié à un bug réel. J'aimerai demander plus facilement de l'aide.
|
||||
- J'ai mal planifié mes journées et entre autre l'articulation avec les soirées. Il faut que je me mette des gardes fous pour éviter de trop dépasser sur le soir, surtout en travaillant avec d'autres Timezones.
|
||||
|
||||
## Vu, Lu, etc
|
||||
|
||||
Des discussions autour de quel statut choisir pour des organisations de type SCOP ou CAE m'ont emmené à redécouvrir les ressources sur le sujet:
|
||||
|
||||
- [https://dtc-innovation.github.io/writings/2017/une-association](https://dtc-innovation.github.io/writings/2017/une-association) je me rends compte que j'avais mal retenu, et que c'est bel et bien une association à but **non lucratif**, mais par contre **à activité commerciale et gestion intéressée** (gloups!)**.**
|
||||
- Sur le rapport à la subordination, [cet article](https://github.com/lechappeebelle/association/blob/principale/_posts/2020/11/20/travailler-ensemble-sans-subordination.md), un peu plus récent, par une partie des mêmes gens. Je trouve ça hyper intéressant !
|
||||
- Et aussi [cet épisode de podcast](https://www.questions-asso.com/episodes/s02/episode6.html) dans lequel Maïtané en parle (je découvre le podcast)
|
||||
- 🎵 Découvert [Macklemore - HIND’S HALL](https://www.youtube.com/watch?v=Lpyl21JH6mA&rco=1), ça fait du bien de voir un artiste aussi connu se positionner clairement contre la guerre en palestine.
|
||||
- 🎵 Découvert [Maggie Rogers](https://youtu.be/SqPtIkxSxI0?si=Kl9djIteNEyUX_kd&t=548)
|
||||
- 🎵 Découvert l'EP de Lisa Leblanc, qui date un peu mais très punchy, ça fait un bien fou. Banjo ! [Highways, Heartaches and Time Well Wasted - EP par Lisa LeBlanc](https://www.youtube.com/watch?v=B8Mes-Z-tVo&list=OLAK5uy_nm5AyPR4VeXmSro_MHIU5h5XZSlMg9Tq4&index=2)
|
||||
- 🇺🇸 [Bragg thanks jury - Live Updates - POLITICO](https://www.politico.com/live-updates/2024/05/30/trump-hush-money-criminal-trial/alvin-bragg-thanks-jury-00160878) Finalement Trump est condamné, et ça semble presque être une aubaine pour lui. 🤦🏼♂️
|
||||
- 🔒 [The CDN is Dead, Long Live the CDN! - Cache Partitioning in Firefox and Chrome](https://www.peakhour.io/blog/cache-partitioning-firefox-chrome/)
|
||||
- 🕸️ [Generative AI Is Totally Shameless. I Want to Be It | WIRED](https://www.wired.com/story/generative-ai-totally-shameless/)
|
||||
- ▶︎ [Loop - ARTE Court Métrage | ARTE - YouTube](https://www.youtube.com/watch?v=I496UVX49e0)
|
||||
|
||||
### Containers
|
||||
|
||||
> It’s important to note that if you try to run a container image with a different architecture than your host machine, Podman will not be able to execute the container. For example, if you try to run an ARM64 container image on a host machine with an AMD64 or x86 architecture, Podman will not be able to execute the container.
|
||||
> [Switching from Docker Desktop to Podman on macOS M1/M2 ARM64 CPU | by (λx.x)eranga | Effectz.AI | Medium](https://medium.com/rahasak/switching-from-docker-desktop-to-podman-on-macos-m1-m2-arm64-cpu-7752c02453ec)
|
||||
|
||||
Je ne comprends encore pas très bien comment rosetta permet de faire la traduction des instructions X86 en ARM64, et en quoi ça me permet de faire tourner certaines images prévues pour X86 et pas d'autres. Je m'accroche. ⛏️
|
||||
|
||||
## Démocratie et IA
|
||||
|
||||
> AIs can also write laws. In November 2023, Porto Alegre, Brazil became the first city to enact a law that was entirely written by AI. It had to do with water meters. One of the councilmen prompted ChatGPT, and it produced a complete bill. He submitted it to the legislature without telling anyone who wrote it. And the humans passed it without any changes.
|
||||
>
|
||||
> Imagine that we train an AI on lots of street camera footage to recognize reckless driving and that it gets better than humans at identifying the sort of behavior that tends to result in accidents. And because it has real-time access to cameras everywhere, it can spot it everywhere. **The AI won’t be able to explain its criteria: It would be a black-box neural net. But we could pass a law defining reckless driving by what that AI says**. It would be a law that no human could ever understand. This could happen in all sorts of areas where judgment is part of defining what is illegal. We could delegate many things to the AI because of speed and scale. Market manipulation. Medical malpractice. False advertising. I don’t know if humans will accept this.
|
||||
>
|
||||
> — [How AI Will Change Democracy - Schneier on Security](https://www.schneier.com/blog/archives/2024/05/how-ai-will-change-democracy.html)
|
||||
|
||||
|
|
@ -30,14 +30,14 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<td>Month</td>
|
||||
<td>Days</td>
|
||||
<td>Hours</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for month, hours in page.metadata.worklog.monthly_hours.items() %}
|
||||
<tr>
|
||||
<td>{{ month }}</td>
|
||||
<td>{{ (hours['payed'] / 7.0) | round(1) }}</td>
|
||||
<td>{{ hours['payed'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -11,10 +11,15 @@ from pelican import signals
|
|||
from pelican.readers import Markdown, MarkdownReader, pelican_open
|
||||
from pelican.utils import get_date, slugify
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_TIME, "fr_FR.UTF8")
|
||||
except Exception:
|
||||
locale.setlocale(locale.LC_TIME, "fr_FR")
|
||||
|
||||
def set_locale(dest):
|
||||
try:
|
||||
locale.setlocale(locale.LC_TIME, f"{dest}.UTF8")
|
||||
except Exception:
|
||||
locale.setlocale(locale.LC_TIME, f"{dest}")
|
||||
|
||||
|
||||
set_locale("fr_FR")
|
||||
|
||||
|
||||
class WorklogPreprocessor(Preprocessor):
|
||||
|
@ -43,6 +48,7 @@ class WorklogPreprocessor(Preprocessor):
|
|||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, lines):
|
||||
# set_locale('en_US')
|
||||
new_lines = []
|
||||
for line in lines:
|
||||
if line.startswith("##"):
|
||||
|
@ -78,6 +84,7 @@ class WorklogPreprocessor(Preprocessor):
|
|||
new_lines.append(f"## 🗓️ {displayed_date}")
|
||||
else:
|
||||
new_lines.append(line)
|
||||
# set_locale('fr_FR')
|
||||
return new_lines
|
||||
|
||||
def compute_data(self, metadata):
|
||||
|
@ -169,6 +176,7 @@ class SimpleReader(MarkdownReader):
|
|||
os.path.abspath(os.path.join(source_path, os.pardir))
|
||||
)
|
||||
metadata["category"] = self.process_metadata("category", category)
|
||||
|
||||
return content, metadata
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue