diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md index 566b92e9b..0f4498d17 100644 --- a/docs/src/markdown/about/changelog.md +++ b/docs/src/markdown/about/changelog.md @@ -1,5 +1,10 @@ # Changelog +## 10.10 + +- **NEW**: MagicLink: Change social process to support `x` instead of `twitter`. `twitter` is still recognized but is + now deprecated and will be removed at a future time. + ## 10.9 - **NEW**: Officially support Python 3.13. diff --git a/docs/src/markdown/extensions/magiclink.md b/docs/src/markdown/extensions/magiclink.md index d4cc7182e..f101291f5 100644 --- a/docs/src/markdown/extensions/magiclink.md +++ b/docs/src/markdown/extensions/magiclink.md @@ -90,7 +90,7 @@ requests (!13{.magiclink-ignore}), GitHub Discussion (?1173{.magiclink-ignore}), (7d1b1902ea7fe00043a249564ed5032f08dd7152{.magiclink-ignore}), and compares (e2ed7e0b3973f3f9eb7a26b8ef7ae514eebfe0d2...90b6fb8711e75732f987982cc024e9bb0111beac{.magiclink-ignore}). You can also reference repositories (@facelessuser/pymdown-extensions{.magiclink-ignore}) and users -(@facelessuser{.magiclink-ignore}). Mentions also works for social media (only Twitter is supported at this time). +(@facelessuser{.magiclink-ignore}). Mentions also works for social media (only X is supported at this time). ``` The syntax used is actually very similar to GitLab's syntax. GitLab was chosen as its syntax bridges the gaps between @@ -127,7 +127,7 @@ default, use the format `@{provider}:{user}` ```text title="Mentions" @facelessuser -@twitter:twitter +@x:x ``` /// html | div.result @@ -149,7 +149,7 @@ extension_configs: --- @facelessuser -@twitter:twitter +@x:x ``` /// @@ -496,7 +496,7 @@ Compares | `magiclink-compare` GitHub | `magiclink-github` Bitbucket | `magiclink-bitbucket` GitLab | `magiclink-gitlab` -Twitter | `magiclink-twitter` +X | `magiclink-x` /// tip | Styling Links With a little bit of CSS^[†](#_fn_1)^, you can also add icons in front: 7d1b1902ea7fe00043a249564ed5032f08dd7152, @@ -515,7 +515,7 @@ Option | Type | Default | Descrip ------------------------------- | ------ | --------------------------- | ----------- `hide_protocol` | bool | `#!py3 False` | If `True`, links are displayed without the initial `ftp://`, `http://`, `https://`, or `ftps://`. `repo_url_shortener` | bool | `#!py3 False` | If `True`, GitHub, Bitbucket, and GitLab commit, pull, and issue links are are rendered in a shorthand syntax. -`social_url_shortener` | bool | `#!py3 False` | if `True`, Twitter user links are rendered in a shorthand syntax. +`social_url_shortener` | bool | `#!py3 False` | if `True`, X user links are rendered in a shorthand syntax. `shortener_user_exclude` | dict | [See below](#user-excludes) | Specifies a list of user names to avoid when attempting to shorten links. See [User Excludes](#user-excludes) for more info. `repo_url_shorthand` | bool | `#!py3 False` | If `True`, you can directly use a shorthand syntax to represent commit, pull, issue, and mention links for repository providers and they will be auto-linked. `social_url_shorthand` | bool | `#!py3 False` | If `True`, you can directly use a shorthand syntax to represent mention links for social media providers and they will be auto-linked. @@ -534,7 +534,7 @@ Defaults for `shortener_user_exclude`: "bitbucket": ['dashboard', 'account', 'plans', 'support', 'repo'], "github": ['marketeplace', 'notifications', 'issues', 'pull', 'sponsors', 'settings', 'support'], "gitlab": ['dashboard', '-', 'explore', 'help', 'projects'], - "twitter": ['i', 'messages', 'bookmarks', 'home'] + "x": ['i', 'messages', 'bookmarks', 'home'] } ``` diff --git a/pymdownx/__meta__.py b/pymdownx/__meta__.py index e4a20b05b..5e8aa375f 100644 --- a/pymdownx/__meta__.py +++ b/pymdownx/__meta__.py @@ -185,5 +185,5 @@ def parse_version(ver, pre=False): return Version(major, minor, micro, release, pre, post, dev) -__version_info__ = Version(10, 9, 0, "final") +__version_info__ = Version(10, 10, 0, "final") __version__ = __version_info__._get_canonical() diff --git a/pymdownx/magiclink.py b/pymdownx/magiclink.py index 4e0c3b587..d4c450cce 100644 --- a/pymdownx/magiclink.py +++ b/pymdownx/magiclink.py @@ -26,6 +26,7 @@ from markdown import Extension from markdown.treeprocessors import Treeprocessor from markdown import util as md_util +from .util import warn_deprecated import xml.etree.ElementTree as etree from . import util import re @@ -38,7 +39,8 @@ "bitbucket": ['dashboard', 'account', 'plans', 'support', 'repo'], "github": ['marketeplace', 'notifications', 'issues', 'pull', 'sponsors', 'settings', 'support'], "gitlab": ['dashboard', '-', 'explore', 'help', 'projects'], - "twitter": ['i', 'messages', 'bookmarks', 'home'] + "twitter": ['i', 'messages', 'bookmarks', 'home'], + "x": ['i', 'messages', 'bookmarks', 'home'] } # Bare link/email detection @@ -68,6 +70,7 @@ # Provider specific user regex rules RE_TWITTER_USER = r'\w{1,15}' +RE_X_USER = r'\w{1,15}' RE_GITHUB_USER = r'[a-zA-Z\d](?:[-a-zA-Z\d_]{0,37}[a-zA-Z\d])?' RE_GITLAB_USER = r'[\.a-zA-Z\d_](?:[-a-zA-Z\d_\.]{0,37}[-a-zA-Z\d_])?' RE_BITBUCKET_USER = r'[-a-zA-Z\d_]{1,39}' @@ -94,6 +97,7 @@ def create_ext_mentions(name, provider_type): return fr'{name}:{RE_BITBUCKET_USER}' RE_TWITTER_EXT_MENTIONS = fr'twitter:{RE_TWITTER_USER}' +RE_X_EXT_MENTIONS = fr'x:{RE_X_USER}' RE_GITHUB_EXT_MENTIONS = create_ext_mentions('github', 'github') RE_GITLAB_EXT_MENTIONS = create_ext_mentions('gitlab', 'gitlab') RE_BITBUCKET_EXT_MENTIONS = create_ext_mentions('bitbucket', 'bitbucket') @@ -245,13 +249,14 @@ def create_user_link_pattern(provider, host, www=True): RE_SOCIAL_LINK = re.compile( r'''(?xi) ^(?: - (?P(?Phttps://(?:w{{3}}\.)?twitter\.com/(?P{}))) + (?P(?Phttps://(?:w{{3}}\.)?twitter\.com/(?P{}))) | + (?P(?Phttps://(?:w{{3}}\.)?x\.com/(?P{}))) )/?$ - '''.format(RE_TWITTER_USER) + '''.format(RE_TWITTER_USER, RE_X_USER) ) # Provider specific info (links, names, specific patterns, etc.) -SOCIAL_PROVIDERS = {'twitter'} +SOCIAL_PROVIDERS = {'x', 'twitter'} # Templates for providers PROVIDER_TEMPLATES = { @@ -295,6 +300,12 @@ def create_user_link_pattern(provider, host, www=True): "url": "{}", "user_pattern": RE_TWITTER_USER }, + "x": { + "provider": "X", + "type": "x", + "url": "{}", + "user_pattern": RE_X_USER + } } @@ -311,6 +322,7 @@ def create_provider(provider, host): PROVIDER_INFO = { "twitter": create_provider('twitter', "https://twitter.com"), + "x": create_provider('x', "https://x.com"), "gitlab": create_provider('gitlab', 'https://gitlab.com'), "bitbucket": create_provider('bitbucket', "https://bitbucket.org"), "github": create_provider('github', "https://github.com") @@ -643,6 +655,9 @@ def get_social_provider(self, match): if match.group('twitter'): provider = 'twitter' + + elif match.group('x'): + provider = 'x' return provider def get_type(self, provider, match): @@ -802,6 +817,8 @@ def run(self, root): m = RE_SOCIAL_LINK.match(href) if m: provider = self.get_social_provider(m) + if provider == 'twitter': + warn_deprecated("The 'twitter' social provider has been deprecated, please use 'x' instead") self.my_repo = self.is_my_repo(provider, m) self.my_user = self.my_repo or self.is_my_user(provider, m) value, link_type = self.get_type(provider, m) @@ -896,6 +913,9 @@ def handleMatch(self, m, data): provider = self.provider mention = parts[0] + if provider == 'twitter': + warn_deprecated("The 'twitter' social provider has been deprecated, please use 'x' instead") + el = etree.Element("a") el.set('href', '{}/{}'.format(self.provider_info[provider]['url'], mention)) el.set( @@ -1041,7 +1061,8 @@ def __init__(self, *args, **kwargs): "bitbucket": ['dashboard', 'account', 'plans', 'support', 'repo'], "github": ['marketeplace', 'notifications', 'issues', 'pull', 'sponsors', 'settings', 'support'], "gitlab": ['dashboard', '-', 'explore', 'help', 'projects'], - "twitter": ['i', 'messages', 'bookmarks', 'home'] + "twitter": ['i', 'messages', 'bookmarks', 'home'], + "x": ['i', 'messages', 'bookmarks', 'home'] }, "A list of user names to exclude from URL shortening." ], @@ -1055,7 +1076,7 @@ def __init__(self, *args, **kwargs): ], 'provider': [ 'github', - 'The base provider to use (github, gitlab, bitbucket, twitter) - Default: "github"' + 'The base provider to use (github, gitlab, bitbucket, x) - Default: "github"' ], 'labels': [ {}, @@ -1294,6 +1315,7 @@ def extendMarkdown(self, md): self.ext_mentions.extend(external_users) if self.social_short: + self.ext_mentions.append(RE_X_EXT_MENTIONS) self.ext_mentions.append(RE_TWITTER_EXT_MENTIONS) self.int_mentions = self.provider_info[self.provider]['user_pattern'] self.setup_shorthand(md) diff --git a/tests/extensions/magiclink/magiclink (shorthand).html b/tests/extensions/magiclink/magiclink (shorthand).html index 4b250bbd0..0dfc1f050 100644 --- a/tests/extensions/magiclink/magiclink (shorthand).html +++ b/tests/extensions/magiclink/magiclink (shorthand).html @@ -21,7 +21,7 @@

Commit some-user/some-repo@3f6b07a8

Compare some-user/some-repo@e2ed7e0b...90b6fb87


-

@username

+

@username


No issue #33

No issue #

diff --git a/tests/extensions/magiclink/magiclink (shorthand).txt b/tests/extensions/magiclink/magiclink (shorthand).txt index 2e37439f5..86a036be6 100644 --- a/tests/extensions/magiclink/magiclink (shorthand).txt +++ b/tests/extensions/magiclink/magiclink (shorthand).txt @@ -44,7 +44,7 @@ Compare gitlab:some-user/some-repo@e2ed7e0b3973f3f9eb7a26b8ef7ae514eebfe0d2...90 --- -@twitter:username +@x:username --- diff --git a/tests/extensions/magiclink/magiclink (shorthand-social).html b/tests/extensions/magiclink/magiclink (shorthand-social).html index 065d77ed2..7c0b1cd40 100644 --- a/tests/extensions/magiclink/magiclink (shorthand-social).html +++ b/tests/extensions/magiclink/magiclink (shorthand-social).html @@ -1,6 +1,6 @@ -

Mention @some-bodies_name

-

Not a repo mention @some-bodies_name/some_repository

-

Not a repo mention @facelessuser/some_repository

+

Mention @some-bodies_name

+

Not a repo mention @some-bodies_name/some_repository

+

Not a repo mention @facelessuser/some_repository

No Commit some-bodies_name/some_repository@3f6b07a8eeaa9d606115758d90f55fec565d4e2a

No Issue some-bodies_name/some_repository#33

No Pull request some-bodies_name/some_repository!33

@@ -17,7 +17,7 @@

Pull request some-user/some-repo!2

Commit some-user/some-repo@3f6b07a8


-

@username

+

@username


No issue #33

No pull request !33

diff --git a/tests/extensions/magiclink/magiclink (shorthand-social).txt b/tests/extensions/magiclink/magiclink (shorthand-social).txt index 3a6988031..e63373e42 100644 --- a/tests/extensions/magiclink/magiclink (shorthand-social).txt +++ b/tests/extensions/magiclink/magiclink (shorthand-social).txt @@ -36,7 +36,7 @@ Commit gitlab:some-user/some-repo@3f6b07a8eeaa9d606115758d90f55fec565d4e2a --- -@twitter:username +@x:username --- diff --git a/tests/extensions/magiclink/tests.yml b/tests/extensions/magiclink/tests.yml index c552f8b30..fe76cc723 100644 --- a/tests/extensions/magiclink/tests.yml +++ b/tests/extensions/magiclink/tests.yml @@ -70,7 +70,7 @@ magiclink (shorthand-social): repo_url_shortener: true repo_url_shorthand: true social_url_shorthand: true - provider: twitter + provider: x user: facelessuser repo: pymdown-extensions diff --git a/tests/test_extensions/test_magiclink.py b/tests/test_extensions/test_magiclink.py index 872dbab6f..4618ba3de 100644 --- a/tests/test_extensions/test_magiclink.py +++ b/tests/test_extensions/test_magiclink.py @@ -1,6 +1,7 @@ """Test cases for MagicLink.""" from .. import util import markdown +import warnings class TestMagicLinkShortner(util.MdCase): @@ -38,8 +39,8 @@ def test_no_social(self): """Test that social shortening does not happen.""" self.check_markdown( - r'https://twitter.com/someuser', - r'

https://twitter.com/someuser

' + r'https://x.com/someuser', + r'

https://x.com/someuser

' ) def test_excluded_user(self): @@ -142,8 +143,8 @@ def test_user(self): # Test #! original syntax self.check_markdown( - r'https://twitter.com/someuser', - r'

@someuser

' # noqa: E501 + r'https://x.com/someuser', + r'

@someuser

' # noqa: E501 ) def test_no_repo(self): @@ -158,8 +159,8 @@ def test_excluded(self): """Test excluded user.""" self.check_markdown( - r'https://twitter.com/home', - r'

https://twitter.com/home

' + r'https://x.com/home', + r'

https://x.com/home

' ) @@ -340,3 +341,61 @@ def test_bad_name(self): with self.assertRaises(ValueError): markdown.markdown('', extensions=extension, extension_configs=extension_configs) + +class TestMagicLinkWarning(util.MdCase): + """Test cases for social link shortener.""" + + extension = [ + 'pymdownx.magiclink' + ] + + extension_configs = { + 'pymdownx.magiclink': { + 'social_url_shortener': True, + 'social_url_shorthand': True + } + } + + def test_deprecated_twitter(self): + """Test deprecation warning.""" + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + + # Trigger a warning. + warnings.simplefilter("always") + self.check_markdown( + '@twitter:user', + '

@user

' # noqa: E501 + ) + target = "The 'twitter' social provider has been deprecated, please use 'x' instead" + for warn in w: + if target in str(warn.message): + found = True + break + # Verify some things + self.assertTrue(len(w) == 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertTrue(found) + + def test_deprecated_twitter_shortener(self): + """Test shortener deprecation warning.""" + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + + # Trigger a warning. + warnings.simplefilter("always") + self.check_markdown( + 'https://twitter.com/user', + '

@user

' # noqa: E501 + ) + target = "The 'twitter' social provider has been deprecated, please use 'x' instead" + for warn in w: + if target in str(warn.message): + found = True + break + # Verify some things + self.assertTrue(len(w) == 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertTrue(found)