From bb5eb5445ea614e0b5e4990959fbe4fb56d1e1d9 Mon Sep 17 00:00:00 2001 From: cristoffel Date: Mon, 6 Nov 2023 18:58:32 +0100 Subject: [PATCH 1/3] use flit packaging tool, write simple test --- .gitignore | 5 +- README.md | 13 ++ flake.lock | 27 +++ flake.nix | 29 +++ mdx_oembed/__init__.py | 9 - pyproject.toml | 46 +++++ .../__init__.py | 0 .../endpoints.py | 1 - .../inlinepatterns.py | 2 - .../oembedextension.py | 8 +- .../tests/test_expectedHtml.html | 9 + .../tests/test_markdown.md | 12 ++ .../tests/test_markdown.py | 21 ++ .../tests/vimeoMock.yaml | 30 +++ tests.py | 192 ------------------ 15 files changed, 195 insertions(+), 209 deletions(-) create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100644 mdx_oembed/__init__.py create mode 100644 pyproject.toml rename mdx_oembed/version.py => src/python_markdown_oembed_extension/__init__.py (100%) rename {mdx_oembed => src/python_markdown_oembed_extension}/endpoints.py (96%) rename {mdx_oembed => src/python_markdown_oembed_extension}/inlinepatterns.py (97%) rename mdx_oembed/extension.py => src/python_markdown_oembed_extension/oembedextension.py (81%) create mode 100644 src/python_markdown_oembed_extension/tests/test_expectedHtml.html create mode 100644 src/python_markdown_oembed_extension/tests/test_markdown.md create mode 100644 src/python_markdown_oembed_extension/tests/test_markdown.py create mode 100644 src/python_markdown_oembed_extension/tests/vimeoMock.yaml delete mode 100644 tests.py diff --git a/.gitignore b/.gitignore index 4e17e26..6b21a0f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ .idea /dist /.eggs -/venv +venv *.pyc *.egg *.egg-info +*.coverage +*.swp +*.swo diff --git a/README.md b/README.md new file mode 100644 index 0000000..250d4e6 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Python Markdown Plugin for oEmbed + +## Install flit + +``` +pip install flit +``` + +## Build needed packages for project + +``` +flit install +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9b0eab5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ffb8b7d --- /dev/null +++ b/flake.nix @@ -0,0 +1,29 @@ +{ + description = "Oembed plugin flake"; + inputs = { + nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable; + }; + outputs = { self, nixpkgs }: + let + pkgs = import nixpkgs { + inherit system; + overlays = []; + }; + pythonPackages = pkgs.python3Packages; + system = "x86_64-linux"; + in rec { + devShell.x86_64-linux = pkgs.mkShell { + buildInputs = [ + pkgs.python3 + pkgs.python3Packages.pip + ]; + shellHook = '' + export PS1='\u@md-oembed \$ ' + export PIP_PREFIX=$(pwd)/venv/pip_packages + export PYTHONPATH="$PIP_PREFIX/${pkgs.python3.sitePackages}:$PYTHONPATH" + export PATH="$PIP_PREFIX/bin:$PATH" + unset SOURCE_DATE_EPOCH + ''; + }; + }; +} diff --git a/mdx_oembed/__init__.py b/mdx_oembed/__init__.py deleted file mode 100644 index b783ea7..0000000 --- a/mdx_oembed/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -from mdx_oembed.extension import OEmbedExtension - - -VERSION = '0.2.1' - - -def makeExtension(**kwargs): - return OEmbedExtension(**kwargs) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f85f1f9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[build-system] +build-backend = "flit_core.buildapi" +requires = ["flit_core >=3.8.0,<4"] + +[project] +name = "python_markdown-oembed_extension" +version = "0.0.0" +authors = [ + {name = "nmc", email = "contact-nmc@unibas.ch"}, +] +description = "Detect inline pattern starting with '![', use the following data to request oembed data from the video hoster and create appropriate iframe html" +readme = "README.md" +requires-python = ">=3.7" +keywords = ["markdown", "html", "oembed"] +license = {text = "GPLv3"} +classifiers = [ + "Programming Language :: Python :: 3", +] +dependencies = [ + "python-oembed", + "markdown", + "requests", +] + +[project.optional-dependencies] +dev = [ + "pylint ~=2.14.0", + "toml ~=0.10.2", + "yapf ~=0.32.0", +] +test = [ + "pytest-cov ~=3.0.0", + "requests_mock", + "pyyaml", +] + +[project.scripts] +my-script = "my_package.module:function" + +[tool.coverage.run] +source = ["src"] + +[tool.pytest.ini_options] +addopts = "--cov --cov-report html --cov-report term-missing --cov-fail-under 15" +# ... other project metadata fields as specified in: +# https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ diff --git a/mdx_oembed/version.py b/src/python_markdown_oembed_extension/__init__.py similarity index 100% rename from mdx_oembed/version.py rename to src/python_markdown_oembed_extension/__init__.py diff --git a/mdx_oembed/endpoints.py b/src/python_markdown_oembed_extension/endpoints.py similarity index 96% rename from mdx_oembed/endpoints.py rename to src/python_markdown_oembed_extension/endpoints.py index 6946822..2121332 100644 --- a/mdx_oembed/endpoints.py +++ b/src/python_markdown_oembed_extension/endpoints.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import oembed YOUTUBE = oembed.OEmbedEndpoint('https://www.youtube.com/oembed', [ diff --git a/mdx_oembed/inlinepatterns.py b/src/python_markdown_oembed_extension/inlinepatterns.py similarity index 97% rename from mdx_oembed/inlinepatterns.py rename to src/python_markdown_oembed_extension/inlinepatterns.py index a43f44b..4e394b0 100644 --- a/mdx_oembed/inlinepatterns.py +++ b/src/python_markdown_oembed_extension/inlinepatterns.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import logging from markdown.inlinepatterns import Pattern import oembed @@ -10,7 +9,6 @@ LOG = logging.getLogger(__name__) OEMBED_LINK_RE = r'\!\[([^\]]*)\]\(((?:https?:)?//[^\)]*)' \ r'(?In this video Jakob Zinsstag introduces the topic of the course. You will +discover that the relationship between humans and animals is manifold. +{.lead}

+

+

Have a look at the farm of Jakob Zinsstag’s cousin in the Canton of Jura, +Switzerland. Different animals create different feelings: there are those we +love, some provoke fears and others will be eaten. Jakob Zinsstag shares the +personal experiences he has had with animals.

+

How do you categorise your own experience with animals?

diff --git a/src/python_markdown_oembed_extension/tests/test_markdown.md b/src/python_markdown_oembed_extension/tests/test_markdown.md new file mode 100644 index 0000000..107b350 --- /dev/null +++ b/src/python_markdown_oembed_extension/tests/test_markdown.md @@ -0,0 +1,12 @@ +In this video Jakob Zinsstag introduces the topic of the course. You will +discover that the relationship between humans and animals is manifold. +{.lead} + +![embed](https://vimeo.com/734276368/f29c542352) + +Have a look at the farm of Jakob Zinsstag’s cousin in the Canton of Jura, +Switzerland. Different animals create different feelings: there are those we +love, some provoke fears and others will be eaten. Jakob Zinsstag shares the +personal experiences he has had with animals. + +**How do you categorise your own experience with animals?** diff --git a/src/python_markdown_oembed_extension/tests/test_markdown.py b/src/python_markdown_oembed_extension/tests/test_markdown.py new file mode 100644 index 0000000..4391235 --- /dev/null +++ b/src/python_markdown_oembed_extension/tests/test_markdown.py @@ -0,0 +1,21 @@ +import markdown, yaml, requests_mock +from python_markdown_oembed_extension.oembedextension import OEmbedExtension +from python_markdown_oembed_extension.endpoints import VIMEO + + +def test_full(): + with ( requests_mock.Mocker() as m + , open('./src/python_markdown_oembed_extension/tests/vimeoMock.yaml', 'r') as vm + , open('./src/python_markdown_oembed_extension/tests/test_markdown.md', 'r') as md + , open('./src/python_markdown_oembed_extension/tests/test_expectedHtml.html', 'r') as expectedHtml + ): + + yml = yaml.safe_load(vm) + m.get(yml['request'], json=yml['response']) + + mdString = md.read() + htmlString = markdown.markdown(mdString, extensions=[OEmbedExtension()]) + print(htmlString) + + assert htmlString == expectedHtml.read().rstrip() + diff --git a/src/python_markdown_oembed_extension/tests/vimeoMock.yaml b/src/python_markdown_oembed_extension/tests/vimeoMock.yaml new file mode 100644 index 0000000..4559214 --- /dev/null +++ b/src/python_markdown_oembed_extension/tests/vimeoMock.yaml @@ -0,0 +1,30 @@ +--- +request: 'https://vimeo.com/734276368/f29c542352' +response: + account_type: 'live_premium' + author_name: 'NMC Universität Basel' + author_url: 'https://vimeo.com/newmediacenterunibasel' + description: '' + duration: 282 + height: 240 + html: >- + + is_plus: '0' + provider_name: 'Vimeo' + provider_url: 'https://vimeo.com/' + thumbnail_height: 166 + thumbnail_url: 'https://i.vimeocdn.com/video/1480489232-5ca2d723cadc09ae077c8b437581e84bd0485049780c60e218986fda60881110-d_295x166' + thumbnail_url_with_play_button: 'https://i.vimeocdn.com/filter/overlay?src0=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1480489232-5ca2d723cadc09ae077c8b437581e84bd0485049780c60e218986fda60881110-d_295x166&src1=http%3A%2F%2Ff.vimeocdn.com%2Fp%2Fimages%2Fcrawler_play.png' + thumbnail_width: 295 + title: 'One-Health_Tales_EN_1-02' + type: 'video' + upload_date: '2022-07-28 04:16:03' + uri: '/videos/734276368:f29c542352' + version: '1.0' + video_id: 734276368 + width: 426 diff --git a/tests.py b/tests.py deleted file mode 100644 index 8cc8aad..0000000 --- a/tests.py +++ /dev/null @@ -1,192 +0,0 @@ -# -*- coding: utf-8 -*- -import re -import unittest -import markdown -from mock import patch -from nose.plugins.skip import SkipTest -from mdx_oembed.extension import OEMBED_LINK_RE -from mdx_oembed import endpoints - - -class OEmbedPatternRegexTestCase(unittest.TestCase): - def setUp(self): - self.re = re.compile(OEMBED_LINK_RE) - - def test_ignore_relative_image_link(self): - text = '![image](/image.png)' - match = self.re.match(text) - self.assertIsNone(match) - - def test_ignore_absolute_image_link(self): - text = '![Mumbo Jumbo](http://tannern.com/mumbo-jumbo.jpg)' - match = self.re.match(text) - self.assertIsNone(match) - - def test_ignore_png_image_link(self): - text = '![Mumbo Jumbo](http://tannern.com/mumbo-jumbo.png)' - match = self.re.match(text) - self.assertIsNone(match) - - def test_ignore_jpg_image_link(self): - text = '![Mumbo Jumbo](http://tannern.com/mumbo-jumbo.jpg)' - match = self.re.match(text) - self.assertIsNone(match) - - def test_ignore_gif_image_link(self): - text = '![Mumbo Jumbo](http://tannern.com/mumbo-jumbo.gif)' - match = self.re.match(text) - self.assertIsNone(match) - - def test_find_youtube_link(self): - text = '![video](http://www.youtube.com/watch?v=7XzdZ4KcI8Y)' - match = self.re.match(text) - self.assertIsNotNone(match) - - def test_find_youtube_short_link(self): - text = '![video](http://youtu.be/7XzdZ4KcI8Y)' - match = self.re.match(text) - self.assertIsNotNone(match) - - def test_find_youtube_http(self): - text = '![video](http://youtu.be/7XzdZ4KcI8Y)' - match = self.re.match(text) - self.assertIsNotNone(match) - - def test_find_youtube_https(self): - text = '![video](https://youtu.be/7XzdZ4KcI8Y)' - match = self.re.match(text) - self.assertIsNotNone(match) - - def test_find_youtube_auto(self): - text = '![video](//youtu.be/7XzdZ4KcI8Y)' - match = self.re.match(text) - self.assertIsNotNone(match) - - -class OEmbedExtensionTestCase(unittest.TestCase): - def setUp(self): - self.markdown = markdown.Markdown(extensions=['oembed']) - - def assert_convert(self, text, expected): - with patch('oembed.OEmbedEndpoint') as MockOEmbedEndpoint: - MockOEmbedEndpoint.get.return_value = expected - output = self.markdown.convert(text) - self.assertEqual(output, expected) - - -class IgnoredTestCase(OEmbedExtensionTestCase): - """ - The OEmbedExtension should ignore these tags allowing markdown's image - processor to find and handle them. - """ - - def test_relative(self): - text = '![alt](image.png)' - expected = '

alt

' - output = self.markdown.convert(text) - self.assertEqual(output, expected) - - def test_slash_relative(self): - text = '![alt](/image.png)' - expected = '

alt

' - output = self.markdown.convert(text) - self.assertEqual(output, expected) - - def test_absolute(self): - text = '![Mumbo Jumbo](http://tannern.com/mumbo-jumbo.jpg)' - expected = '

Mumbo Jumbo

' - output = self.markdown.convert(text) - self.assertEqual(output, expected) - - -class ProtocolVarietyTestCase(OEmbedExtensionTestCase): - - def test_http(self): - text = '![video](http://www.youtube.com/watch?v=7XzdZ4KcI8Y)' - output = self.markdown.convert(text) - self.assertIn(' Date: Tue, 7 Nov 2023 16:33:46 +0100 Subject: [PATCH 2/3] Update __init__.py --- src/python_markdown_oembed_extension/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/python_markdown_oembed_extension/__init__.py b/src/python_markdown_oembed_extension/__init__.py index e69de29..52236bb 100644 --- a/src/python_markdown_oembed_extension/__init__.py +++ b/src/python_markdown_oembed_extension/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +from python_markdown_oembed_extension.extension import OEmbedExtension + + +VERSION = '0.2.2' + + +def makeExtension(**kwargs): + return OEmbedExtension(**kwargs) From f3963e7ad55f431b7df849aad57304039b3111ca Mon Sep 17 00:00:00 2001 From: Benedikt Willi Date: Tue, 7 Nov 2023 16:40:00 +0100 Subject: [PATCH 3/3] Update __init__.py --- src/python_markdown_oembed_extension/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_markdown_oembed_extension/__init__.py b/src/python_markdown_oembed_extension/__init__.py index 52236bb..b733a04 100644 --- a/src/python_markdown_oembed_extension/__init__.py +++ b/src/python_markdown_oembed_extension/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from python_markdown_oembed_extension.extension import OEmbedExtension +from python_markdown_oembed_extension.oembedextension import OEmbedExtension VERSION = '0.2.2'