') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Adds shebang_templates configuration for overriding shebangs. by zooba · Pull Request #359 · python/pymanager · GitHub
Skip to content

Adds shebang_templates configuration for overriding shebangs. - #359

Merged
zooba merged 5 commits into
python:mainfrom
zooba:gh-307
Jun 4, 2026
Merged

Adds shebang_templates configuration for overriding shebangs.#359
zooba merged 5 commits into
python:mainfrom
zooba:gh-307

Conversation

@zooba

@zoobazooba commented Jun 2, 2026

Copy link
Copy Markdown
Member

Fixes#307
Fixes#348

@zooba

zooba commented Jun 2, 2026

Copy link
Copy Markdown
MemberAuthor

Basically just waiting on more tests, but I also haven't tested thoroughly yet so I'm sure it'll break in places. Figured I'd put it out in the open now though - I'm hoping to get it done quickly and put out a beta with it (and the other recent changes).

@zooba
zooba marked this pull request as ready for review June 3, 2026 20:50
@zooba
zooba requested a review from CopilotJune 3, 2026 21:55

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable shebang_templates support so users can override/route non-standard (and POSIX-style) shebangs via pymanager.json, improving compatibility with legacy scripts and cross-platform shebang conventions.

Changes:

  • Introduces shebang_templates config schema/defaults and wires it into shebang parsing.
  • Adds _replace_templates() and integrates it into _parse_shebang() to rewrite or directly resolve template-based shebangs.
  • Expands tests and test config stubs to cover the new config field and parsing paths.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
tests/test_scriptutils.pyAdds tests for shebang template replacement and _parse_shebang integration.
tests/test_install_command.pyUpdates test command stub to include shebang_templates.
tests/conftest.pyUpdates fake config to include shebang_templates and aligns get_install_to_run signature with production.
src/pymanager.jsonAdds default shebang_templates mappings.
src/manage/scriptutils.pyImplements template replacement and integrates it into shebang parsing/warnings.
src/manage/commands.pyAdds config schema + BaseCommand default for shebang_templates; adjusts default source_settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/manage/scriptutils.py Outdated
Comment threadsrc/manage/scriptutils.py
Comment threadsrc/pymanager.json
Comment threadsrc/manage/scriptutils.py
Comment threadtests/test_scriptutils.py
zoobaand others added 2 commits June 3, 2026 23:09
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Steve Dower <steve.dower@microsoft.com>
@zooba
zooba merged commit 080ed44 into python:mainJun 4, 2026
4 checks passed
@zooba
zooba deleted the gh-307 branch June 4, 2026 15:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PurePath.match() incorrectly intercepts and breaks absolute shebangs shebang backwards compatibility

2 participants

@zooba