#35371: JS module export regex does not handle certain minified code correctly
-----------------------------------------------+------------------------
               Reporter:  Michael              |          Owner:  nobody
                   Type:  Uncategorized        |         Status:  new
              Component:  contrib.staticfiles  |        Version:  5.0
               Severity:  Normal               |       Keywords:
           Triage Stage:  Unreviewed           |      Has patch:  0
    Needs documentation:  0                    |    Needs tests:  0
Patch needs improvement:  0                    |  Easy pickings:  0
                  UI/UX:  0                    |
-----------------------------------------------+------------------------
 This regex from django/contrib/staticfiles/storage.py -> HashedFilesMixin
 -> _js_module_import_aggregation_patterns:
 {{{
             (
                 (
                     r"""(?P<matched>export(?s:(?P<exports>[\s\{].*?))"""
                     r"""\s*from\s*["'](?P<url>[./].*?)["']\s*;)"""
                 ),
                 """export%(exports)s from "%(url)s";""",
             ),
 }}}

 Can result in a matchobj like this:
 {{{

 > /home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/storage.py(225)converter()

 (Pdb) pp matchobj.re
 
re.compile('(?P<matched>export(?s:(?P<exports>[\\s\\{].*?))\\s*from\\s*["\'](?P<url>[./].*?)["\']\\s*;)',
 re.IGNORECASE)

 (Pdb) pp matchobj.groupdict()
 {'exports': ' async function getTakeMessageDialog(a,t={}){const '
             'e=f();e.toggleAttribute("top",!0);const '
             'i=document.createElement("wc-take-
 
message");for(i.toggleAttribute("caneditcontacts",a),t&&(i.props=t),e.appendChild(i),e.save(null,{heading:"Take
 '
             'a Message",confirm:"Submit"}),i.focus();;){if(!await '
             'e.waitClickedAction())return;if(!i.validate())continue;const
 '
             'n=await '
 'c.createTakeMessage(i.props);if(n.ok){e.close(),n.notifySuccess("Message
 '
             'taken");return}}}import w',
  'matched': 'export async function getTakeMessageDialog(a,t={}){const '
             'e=f();e.toggleAttribute("top",!0);const '
             'i=document.createElement("wc-take-
 
message");for(i.toggleAttribute("caneditcontacts",a),t&&(i.props=t),e.appendChild(i),e.save(null,{heading:"Take
 '
             'a Message",confirm:"Submit"}),i.focus();;){if(!await '
             'e.waitClickedAction())return;if(!i.validate())continue;const
 '
             'n=await '
 'c.createTakeMessage(i.props);if(n.ok){e.close(),n.notifySuccess("Message
 '
             'taken");return}}}import w from '
             '"/static/skin/skin/x-field.min.c6fe58e9f403.css";',
  'url': '/static/skin/skin/x-field.min.c6fe58e9f403.css'}

 (Pdb) matchobj.string
 'var l=Object.defineProperty;var p=(a,t,e)=>t in
 a?l(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var
 o=(a,t,e)=>(p(a,typeof t!="symbol"?t+"":t,e),e);import*as r
 from"/static/jsapp/jsapp/dtmod.min.js";import*as c
 from"/static/comms/jsapp/fetcher.min.js";import*as g
 from"/static/jsapp/jsapp/ui.min.js";import*as m
 from"/static/jsapp/jsapp/formmod.min.js";import{createDialog as f} from
 "/static/wcapp/wcapp/wc-dialog.min.2f89ad88aab3.js";import{BaseComponent
 as h} from "/static/wcapp/jsapp/wc-base.min.425310100bce.js";class d
 extends
 
h{init(){g.setupAutoHeight(this.get("MESSAGE")),this.initProps(!1)}setupEvents(){this.get("EDIT").onclick=this.setEditMode.bind(this,!0),this.get("CANCEL").onclick=this.setEditMode.bind(this,!1),this.get("SAVE").onclick=async()=>{if(!this.validate())return;(await
 
c.updateTakeMessage(this._props.id,this.props)).ok&&this.setEditMode(!1)}}setEditMode(t){this.toggleAttribute("disabled",!t)}setNoAddContact(){const
 
t=this.get("SEL_ADD_CONTACT").checked;this.toggleAttribute("noaddcontact",!t)}selectContactHandler(t){var
 e;((e=t.target)==null?void
 
0:e.getAttribute("name"))==="select_contact"&&this.setNoAddContact()}attributeChangedCallback(t,e,i){switch(t){case"disabled":this.setDisabled(i!==null);break}}set
 props(t){console.log(t),this._props=t;for(const e of
 
Object.keys(t))switch(e){case"message":this.get("MESSAGE").value=t[e];break;case"createdUtc":this.get("TAKEN_AT").innerHTML=r.formatIsoStr(t[e],r.SHORT_DATETIME);break;case"userName":this.get("TAKEN_BY").innerHTML=t[e];break;case"recipientName":this.get("TAKEN_FOR").innerHTML=t[e];break;case"clientName":const
 
i=t[e];t.client||(this.get("CLIENT_NAME").value=t[e],this.get("CLIENT_NAME").toggleAttribute("required",!0),this.get("CLIENT_NAME_LABEL").classList.remove("d:n"));break;case"client":const
 
s=t[e];s&&(this.get("BADGE").props=s,this.get("BADGE").classList.remove("d:n"));break;case"contact":this.get("CONTACT").props=t[e]}}get
 props(){var i;return{client:(i=this._props.client)==null?void
 
0:i.id,clientName:this.get("CLIENT_NAME").value,message:this.get("MESSAGE").value,contact:this.get("CONTACT").props}}setDisabled(t){this.get("CONTACT").disabled=t,this.get("MESSAGE").disabled=t,this.get("CLIENT_NAME").disabled=t}validate(){const
 t=["CONTACT","MESSAGE"].map(e=>this.get(e));return
 
this.get("CLIENT_NAME").classList.contains("d:n")||t.push(this.get("CLIENT_NAME")),m.manualClientValidation(t)}}o(d,"observedAttributes",["disabled"]),o(d,"properties",["props"]);export
 async function getTakeMessageDialog(a,t={}){const
 e=f();e.toggleAttribute("top",!0);const i=document.createElement("wc-take-
 
message");for(i.toggleAttribute("caneditcontacts",a),t&&(i.props=t),e.appendChild(i),e.save(null,{heading:"Take
 a Message",confirm:"Submit"}),i.focus();;){if(!await
 e.waitClickedAction())return;if(!i.validate())continue;const n=await
 c.createTakeMessage(i.props);if(n.ok){e.close(),n.notifySuccess("Message
 taken");return}}}import w from
 "/static/skin/skin/x-field.min.c6fe58e9f403.css";import u from
 "/static/common/skin/cfm-heading.min.b8e21816b92c.css";const
 b=[w,u];d.initComponent(String.raw`<div id="MAIN" class="d:f f-d:c
 g:--gap"><div id="TAKEN_TABLE" class="d:g (w>768)g-t-c:repeat(6,auto)
 (w<768)g-t-c:auto_auto w:f-c g:8"><div x-field="label">By</div><div
 id="TAKEN_BY" class="m-r:16"></div><div x-field="label">At</div><div
 id="TAKEN_AT" class="m-r:16"></div><div x-field="label">For</div><div
 id="TAKEN_FOR"></div></div><div cfm-heading>Client</div><label
 id="CLIENT_NAME_LABEL" for="CLIENT_NAME" class="d:n d:f? a-i:c f-w:w
 g:8px_16px"><div x-field="label">Client Name</div><input id="CLIENT_NAME"
 x-field="widget tonal stadium" type="text" disabled></label><wc-badge
 id="BADGE" class="d:n w:256 max-w:100%"></wc-badge><div cfm-
 heading>Contact</div><wc-contact disabled id="CONTACT"></wc-contact><div
 class="d:f g:--gap f-w:w"><div class="d:f f-d:c g:--gap f-g:15"><label
 cfm-heading for="MESSAGE">Message</label><textarea id="MESSAGE" rows="4"
 x-field="widget tonal stadium" class="f-g:1" required disable-if-
 disabled></textarea></div></div><div class="f-s:i c:--red" show-if-
 enabled>Please note:<br>The recipient will not be notified or emailed
 again, and any memo\'s or contacts that were originally created from the
 message will remain as is.</div><div class="d:f? m-t:16 j-c:e a-i:c f-w:w
 g:16px_16px" show-if-canedit><wc-button id="EDIT" type="tonal"
 color="primary" show-if-disabled>EDIT</wc-button><wc-button id="CANCEL"
 type="tonal"  color="bw" class="min-w:92" show-if-enabled>CANCEL</wc-
 button><wc-button id="SAVE" type="solid" color="secondary"
 class="min-w:92" show-if-enabled>SAVE</wc-
 button></div></div>\n`,String.raw`:host{display:block}\n*,*:before,*:after
 {box-sizing:border-box}\n::selection{color:var(--selection-fg,
 white);background:var(--selection-bg, #888)}\nsvg{vertical-
 align:bottom}\nsvg,img[src$=".svg"]{flex-shrink:0}\ntextarea{font-
 family:inherit}\n.a-i\\:c{align-
 
items:center!important}\n.c\\:--red{color:var(--red)!important}\n.d\\:f{display:flex!important}\n.d\\:f\\?{display:flex}\n.d\\:g{display:grid!important}\n.d\\:n{display:none!important}\n.f-d\\:c
 {flex-direction:column!important}\n.f-g\\:1{flex-
 grow:1!important}\n.f-g\\:15{flex-grow:15!important}\n.f-s\\:i{font-
 style:italic!important}\n.f-w\\:w{flex-
 
wrap:wrap!important}\n.g\\:--gap{gap:var(--gap)!important}\n.g\\:16px_16px{gap:16px
 16px!important}\n.g\\:8{gap:8px!important}\n.g\\:8px_16px{gap:8px
 16px!important}\n.j-c\\:e{justify-content:end!important}\n.m-r\\:16
 {margin-right:16px!important}\n.m-t\\:16{margin-
 top:16px!important}\n.max-w\\:100\\%{max-
 width:100%!important}\n.min-w\\:92{min-
 width:92px!important}\n.w\\:256{width:256px!important}\n.w\\:f-c{width
 :fit-content!important}\n@media (max-
 width:767.999px){.\\(w\\<768\\)g-t-c\\:auto_auto{grid-template-
 columns:auto auto!important}}\n@media (min-
 width:768.001px){.\\(w\\>768\\)g-t-c\\:repeat\\(6\\,auto\\){grid-template-
 columns:repeat(6,auto)!important}}\n:host{display:grid;grid-template-
 columns:minmax(100%,1024px)}\n:host(:not([disabled])) [show-if-
 disabled]{display:none!important}\n:host([disabled]) [show-if-
 enabled]{display:none!important}\n:host(:not([canedit])) [show-if-
 canedit]{display:none!important}\n`,b,"wc-message-taken",["wc-button","wc-
 select-client","wc-contact","wc-modal"]);\n'

 }}}

 From this Javascript file:

 {{{
 var r=Object.defineProperty;var p=(n,t,e)=>t in
 n?r(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var
 c=(n,t,e)=>(p(n,typeof t!="symbol"?t+"":t,e),e);import*as d
 from"/static/comms/jsapp/fetcher.min.js";import*as h
 from"/static/jsapp/jsapp/ui.min.js";import*as g
 from"/static/jsapp/jsapp/formmod.min.js";import{createDialog as
 f}from"/static/wcapp/wcapp/wc-dialog.min.js";import{BaseComponent as
 u}from"/static/wcapp/jsapp/wc-base.min.js";class l extends
 
u{init(){this._setupPromise=this.setupContext(),h.setupAutoHeight(this.get("MESSAGE")),this.initProps(!1)}setupEvents(){this.get("SELECT_CLIENT").addEventListener("clientChanged",this.changeClientHandler.bind(this)),this.get("SELECT_CLIENT").addEventListener("clientCleared",this.clientClearedHandler.bind(this)),this.get("CONTACTS").addEventListener("click",this.clickButtonHandler.bind(this)),this.get("CONTACTS").addEventListener("change",this.selectContactHandler.bind(this))}setNoAddContact(){const
 
t=this.get("SEL_ADD_CONTACT").checked;this.toggleAttribute("noaddcontact",!t)}selectContactHandler(t){var
 e;((e=t.target)==null?void
 
0:e.getAttribute("name"))==="select_contact"&&this.setNoAddContact()}attributeChangedCallback(t,e,i){switch(t){case"disabled":this.setDisabled(i!==null);break}}set
 props(t){this._props=t}get props(){const
 
t=this.getAttribute("clientid");return{...this._props,client:t?parseInt(t):null,clientName:t?null:this.get("SELECT_CLIENT").pattern,recipient:parseInt(this.get("RECIPIENT").value),message:this.get("MESSAGE").value,contact:this.querySelectedContactEl().props,createMemo:this.get("CREATE_MEMO").checked,addContact:this.get("ADD_CONTACT").checked}}async
 setupContext(){this._context||(this._context=await
 
this.getContext(),this.buildEmployees(this._context.employees),this.buildClients(this._context.clients))}async
 buildEmployees(t){if(this.get("RECIPIENT").firstElementChild)return;const
 e=t.map(s=>{const a=document.createElement("option");return
 
a.value=s.pk,a.innerText=s.fullName,a});this.get("RECIPIENT").replaceChildren(...e);const
 
i=Math.min(e.length,10);!this.hasAttribute("disabled")&&!this.hasAttribute("update")&&this.get("RECIPIENT").setAttribute("size",i),this.get("RECIPIENT").setAttribute("sizebak",i)}async
 buildClients(t){this.get("SELECT_CLIENT").props=t}async
 getContext(){if(this._context)return this._context;const t=await
 d.getContext();if(t.ok)return
 
this._context=t.json,this._context}setDisabled(t){this.shadowRoot.querySelectorAll
 ("[disable-if-
 
disabled]").forEach(i=>i.toggleAttribute("disabled",t)),this.hasAttribute("update")&&this.get("RECIPIENT").toggleAttribute("disabled",!0);const
 
e=this.get("RECIPIENT");this.get("RECIPIENT").hasAttribute("disabled")?e.removeAttribute("size"):e.setAttribute("size",e.getAttribute("sizebak"))}cloneChild(t,e){const
 
i=this.get("CONTACT_TEMPLATE").content.cloneNode(!0).firstElementChild,s=i.querySelector
 ("wc-contact");s.props=t;const
 a=i.querySelector('[name="select_contact"]'),o=i.querySelector("label");return
 
a.id=`contact_${t.hash}`,o.setAttribute("for",a.id),s.disabled=!0,i}getContactOptions(){return
 this._contactOptions}selectContact(t){const
 e=this.get("CONTACTS").querySelector(`[data-hash="${t}"]`),i=e==null?void
 
0:e.querySelector('[name="contact"]');i&&(i.checked=!0)}setContact(t){t.hash?this.selectContact(t.hash):(this.get("NEW_CONTACT").props=t,this.get("ADD_CONTACT").checked=!0)}setContacts(t){this._contactOptions=t||[];const
 
e=this._contactOptions.map((i,s)=>this.cloneChild(i,s));this.get("CONTACTS_SLOT").replaceChildren(...e)}setExistingClient(t){t?this.setAttribute("clientid",t):this.removeAttribute("clientid"),t||(this.get("SEL_ADD_CONTACT").checked=!0),t||(this.get("ADD_CONTACT").checked=!1),this.get("MESSAGE").autoHeight()}changeClientHandler(t){const
 e=t.detail.props;this.setExistingClient(e==null?void 0:e.id);const
 i=(e==null?void
 0:e.hashedContacts)||[];this.setContacts(i)}enableEditContact(t,e){const
 i=e.querySelector("wc-contact");i.disabled=!t,e.querySelector('[data-
 button="edit"]').classList.toggle("d:n",t),e.querySelector('[data-
 button="cancel"]').classList.toggle("d:n",!t),e.querySelector('[data-
 button="save"]').classList.toggle("d:n",!t)}clickButtonHandler(t){const
 e=t.target.getAttribute("data-
 button");if(!e)return;t.stopPropagation();const i=t.target.closest(".js-
 
main");switch(e){case"edit":this.enableEditContact(!0,i);break;case"cancel":this.enableEditContact(!1,i);break;case"save":this.updateContact(i);break}}clientClearedHandler(){this.setExistingClient(!1),this.setContacts([]),this.setNoAddContact()}querySelectedMainEl(){return
 this.get("CONTACTS").querySelector('[name="select_contact"]:checked').closest
 (".js-main")}querySelectedContactEl(){return
 this.querySelectedMainEl().querySelector("wc-
 contact")}focus(){this.get("SELECT_CLIENT").focus()}validate(){const
 t=["SELECT_CLIENT","RECIPIENT","MESSAGE"].map(e=>this.get(e));return
 g.manualClientValidation(t)}async updateContact(t){const
 e=parseInt(this.getAttribute("clientid")),i=t.querySelector("wc-
 contact"),a={contact:i.props},o=await
 
d.clientUpdateContact(e,a);o.ok&&(this.enableEditContact(!1,t),i.props={...i.props,hash:o.json.newHash})}}c(l,"observedAttributes",["disabled"]),c(l,"properties",["props"]);export
 async function getTakeMessageDialog(n,t={}){const
 e=f();e.toggleAttribute("top",!0);const i=document.createElement("wc-take-
 
message");for(i.toggleAttribute("caneditcontacts",n),t&&(i.props=t),e.appendChild(i),e.save(null,{heading:"Take
 a Message",confirm:"Submit"}),i.focus();;){if(!await
 e.waitClickedAction())return;if(!i.validate())continue;const a=await
 
d.createTakeMessage(i.props);if(a.ok){e.close(),setTimeout(()=>a.notifySuccess("Message
 taken"),100);return}}}import m
 from"/static/skin/skin/x-field.min.css";import C from"/static/common/skin
 /cfm-heading.min.css";const b=[m,C];l.initComponent(String.raw`<div
 id="MAIN" class="d:f f-d:c g:--gap"><wc-select-client id="SELECT_CLIENT"
 newtab selectable sel1stsearch patternisnonelabel disable-if-disabled
 ></wc-select-client><div cfm-heading>Contact</div><div id="CONTACTS"
 class="d:f f-d:c"><div class="js-main d:f a-i:c g:8px_16px f-g:1"><input
 id="SEL_ADD_CONTACT" x-field="widget" type="radio" name="select_contact"
 value="new" checked show-if-clientid><div class="d:f f-d:c g:8
 f-g:1"><label for="SEL_ADD_CONTACT" class="f-w:500" show-if-clientid>New
 Contact</label><wc-contact id="NEW_CONTACT" class="f-g:1" show-if-
 addcontact disable-if-disabled></wc-contact><label for="ADD_CONTACT"
 class="d:f g:8 w:f-c m-y:4" show-if-clientid show-if-addcontact show-if-
 caneditcontacts><input id="ADD_CONTACT" type="checkbox" x-field="widget"
 disable-if-disabled><div title="If the client exists, it will be updated
 with this info">Add this contact to the
 client?</div></label></div></div><slot id="CONTACTS_SLOT"
 name="contacts"></slot></div><div class="d:f g:--gap f-w:w"><div
 class="d:f f-d:c g:--gap f-g:10" hide-if-disabled><div cfm-
 heading>Notify</div><select id="RECIPIENT" x-field="widget tonal stadium"
 class="o-y:v max-h:640" disable-if-disabled required></select></div><div
 class="d:f f-d:c g:--gap f-g:15"><label cfm-heading
 for="MESSAGE">Message</label><textarea id="MESSAGE" rows="4"
 x-field="widget tonal stadium" class="f-g:1" required disable-if-
 disabled></textarea><label for="CREATE_MEMO" class="d:f g:8 (w<640)m-y:8"
 hide-if-disabled show-if-clientid><input id="CREATE_MEMO" type="checkbox"
 x-field="widget" disable-if-disabled><div class="js-save-action"
 title="Also create a Client Memo that corresponds to this message">Also
 create a Client Memo?</div></label></div></div></div><template
 id="CONTACT_TEMPLATE"><div class="js-main d:f a-i:c j-c:e c-g:24 r-g:8
 f-w:w"><div class="d:f a-i:c g:8px_16px f-g:10000"><input x-field="widget"
 type="radio" x-field="widget" name="select_contact" disable-if-
 disabled><label x-field="label" class="f-g:1" hide-if-disabled><wc-contact
 ></wc-contact></label></div><div class="d:f a-i:c f-w:w (w>530)f-d:c
 g:16px_16px" hide-if-disabled show-if-caneditcontacts><wc-button
 type="tonal" color="primary"   data-button="edit">EDIT</wc-button><wc-
 button type="text"  color="bw"        data-button="cancel" class="min-w:92
 d:n">CANCEL</wc-button><wc-button type="solid" color="secondary" data-
 button="save"   class="min-w:92 d:n">SAVE</wc-
 button></div></div></template>
 `,String.raw`styles`,b,"wc-take-message",[]);
 }}}


 Which means collect static fails:
 {{{
 Post-processing 'comms/wcapp/wc-take-message.min.js' failed!

 Traceback (most recent call last):
   File "/home/michael/project/src/manage.py", line 57, in <module>
     run()
   File "/home/michael/project/src/manage.py", line 49, in run
     execute_from_command_line(sys.argv)
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/core/management/__init__.py", line 442, in
 execute_from_command_line
     utility.execute()
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/core/management/__init__.py", line 436, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/core/management/base.py", line 413, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/core/management/base.py", line 459, in execute
     output = self.handle(*args, **options)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File
 "/home/michael/project/src/core/app/base/management/commands/deploy.py",
 line 84, in handle
     call_command('collectstatic', '--noinput', '--clear', '--verbosity',
 '0')
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/core/management/__init__.py", line 194, in call_command
     return command.execute(*args, **defaults)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/core/management/base.py", line 459, in execute
     output = self.handle(*args, **options)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/management/commands/collectstatic.py",
 line 209, in handle
     collected = self.collect()
                 ^^^^^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/management/commands/collectstatic.py",
 line 154, in collect
     raise processed
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/storage.py", line 375, in
 _post_process
     content = pattern.sub(converter, content)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/storage.py", line 249, in converter
     hashed_url = self._url(
                  ^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/storage.py", line 182, in _url
     hashed_name = hashed_name_func(*args)
                   ^^^^^^^^^^^^^^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/storage.py", line 425, in _stored_name
     cache_name = self.clean_name(self.hashed_name(name))
                                  ^^^^^^^^^^^^^^^^^^^^^^
   File "/home/michael/.venv/project/lib/python3.11/site-
 packages/django/contrib/staticfiles/storage.py", line 143, in hashed_name
     raise ValueError(
 ValueError: The file 'skin/skin/x-field.min.c6fe58e9f403.css' could not be
 found with <base.storage.LcManifestStaticFilesStorage object at
 0x7f645c578a10>.
 }}}

 Even though the file exists in the correct collect static spot:
 {{{
 :/var/www/example.com/public/static/skin/skin$ ls -la x-field.min.css
 -rw-r--r-- 1 michael www-data 6387 Apr 12 08:07 x-field.min.css
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35371>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018ed6154e44-1635ee3f-2f43-4de7-af68-157c2980d671-000000%40eu-central-1.amazonses.com.

Reply via email to