Error while invoking plonecli: AttributeError: module 'case_conversion' has no attribute 'pascalcase'

This is on a Mac (macOS Sequoia 15.5), with Plone 6.1.2.

I first invoked uvx cookieplone to create a “Backend Add-on for Plone”.

Then, inside the generated directory tree’s src subdirectory, I invoked make add content_type

which gave the error:

AttributeError: module 'case_conversion' has no attribute 'pascalcase'

(see the full error stack at the end of this post)

How can I check which release of case_conversion I have in here? The paths are confusing because uv has its cache, and

% pip show case-conversion
WARNING: Package(s) not found: case-conversion
% make add content_type
==> Generate constraints file
###############################################################################
# Load configuration
Can not parse override:
###############################################################################
# Read infiles
Read [r]: requirements.txt
Read [c]: https://dist.plone.org/release/6.1.2/constraints.txt
###############################################################################
# No sources configured!
###############################################################################
# Write outfiles
Write [c]: constraints-mxdev.txt
Write [r]: requirements-mxdev.txt
🎂 You are now ready for: pip install -r requirements-mxdev.txt
   (path to pip may vary dependent on your installation method)
==> Install environment
Using CPython 3.10.15 interpreter at: /opt/homebrew/opt/python@3.10/bin/python3.10
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
Resolved 291 packages in 3.33s
      Built triangle-notes @ file:///Users/bubba/src/triangle.notes
Prepared 31 packages in 718ms
Installed 291 packages in 1.77s
 + accesscontrol==7.2
 + acquisition==6.1
 + attrs==25.3.0
 + authencoding==6.0
 + babel==2.16.0
 + beautifulsoup4==4.12.3
 + borg-localrole==3.1.11
 + btrees==6.1
 + certifi==2024.12.14
 + cffi==1.17.1
 + chameleon==4.4.4
 + charset-normalizer==3.4.1
 + click==8.2.1
 + collective-mockmailhost==3.0.0
 + collective-monkeypatcher==2.0.0
 + coverage==7.8.2
 + cssselect==1.3.0
 + datetime==5.5
 + decorator==5.2.1
 + diazo==2.0.3
 + documenttemplate==4.6
 + docutils==0.21.2
 + exceptiongroup==1.3.0
 + extensionclass==6.0
 + feedparser==6.0.11
 + five-customerize==4.0
 + five-intid==3.0.1
 + five-localsitemanager==5.0
 + furl==2.1.4
 + grpcio==1.70.0
 + grpcio-tools==1.70.0
 + gunicorn==23.0.0
 + h11==0.16.0
 + horse-with-no-namespace==20250705.0
 + icalendar==6.3.1
 + idna==3.10
 + iniconfig==2.1.0
 + jsonschema==4.24.0
 + jsonschema-specifications==2025.4.1
 + lxml==5.4.0
 + lxml-html-clean==0.4.2
 + markdown==3.8
 + missing==5.0
 + multimapping==5.0
 + multipart==1.2.1
 + natsort==8.4.0
 + orderedmultidict==1.0.1
 + outcome==1.3.0.post0
 + overrides==7.7.0
 + packaging==24.2
 + paste==3.10.1
 + pastedeploy==3.1.0
 + persistence==5.1
 + persistent==6.1
 + piexif==1.1.3
 + pillow==11.2.1
 + plone-alterego==2.0.1
 + plone-api==2.5.2
 + plone-app-caching==4.1.0
 + plone-app-content==4.1.10
 + plone-app-contentlisting==3.0.5
 + plone-app-contentmenu==3.0.7
 + plone-app-contentrules==5.0.7
 + plone-app-contenttypes==4.0.5
 + plone-app-customerize==2.0.2
 + plone-app-dexterity==4.1.0
 + plone-app-discussion==5.2.0
 + plone-app-event==5.2.2
 + plone-app-i18n==4.0.1
 + plone-app-intid==2.0.0
 + plone-app-iterate==6.2.0
 + plone-app-layout==5.0.2
 + plone-app-linkintegrity==4.0.6
 + plone-app-locales==6.0.31
 + plone-app-lockingbehavior==2.0.2
 + plone-app-multilingual==8.3.0
 + plone-app-portlets==6.0.2
 + plone-app-querystring==2.1.3
 + plone-app-redirector==3.0.4
 + plone-app-registry==2.0.6
 + plone-app-relationfield==4.0.2
 + plone-app-robotframework==2.1.5
 + plone-app-testing==7.1.1
 + plone-app-textfield==3.0.0
 + plone-app-theming==5.0.13
 + plone-app-upgrade==3.2.2
 + plone-app-users==3.1.3
 + plone-app-uuid==2.2.3
 + plone-app-versioningbehavior==2.0.4
 + plone-app-viewletmanager==4.0.4
 + plone-app-vocabularies==6.0.2
 + plone-app-workflow==5.0.4
 + plone-app-z3cform==4.7.8
 + plone-autoform==2.0.3
 + plone-autoinclude==2.0.3
 + plone-base==3.1.0
 + plone-batching==2.0.6
 + plone-behavior==2.0.2
 + plone-browserlayer==3.0.2
 + plone-cachepurging==3.0.2
 + plone-caching==2.0.1
 + plone-classicui==1.0.1
 + plone-contentrules==3.0.1
 + plone-dexterity==3.0.7
 + plone-distribution==3.2.0
 + plone-event==2.0.2
 + plone-exportimport==1.1.0
 + plone-folder==4.0.1
 + plone-formwidget-namedfile==3.1.1
 + plone-formwidget-recurrence==3.0.5
 + plone-i18n==5.1.0
 + plone-indexer==2.0.1
 + plone-intelligenttext==4.0.1
 + plone-keyring==4.0.2
 + plone-locking==3.0.2
 + plone-memoize==3.0.3
 + plone-namedfile==7.2.0
 + plone-outputfilters==5.0.4
 + plone-portlet-collection==4.0.4
 + plone-portlet-static==4.0.4
 + plone-portlets==3.0.2
 + plone-protect==5.0.5
 + plone-registry==2.0.1
 + plone-resource==3.0.2
 + plone-resourceeditor==4.0.1
 + plone-rest==5.0.0
 + plone-restapi==9.15.1
 + plone-rfc822==3.0.1
 + plone-scale==4.2.0
 + plone-schema==2.0.2
 + plone-schemaeditor==4.0.11
 + plone-session==4.0.5
 + plone-staticresources==2.3.0
 + plone-stringinterp==2.0.2
 + plone-subrequest==2.0.5
 + plone-supermodel==2.0.4
 + plone-testing==9.0.6
 + plone-theme==4.0.3
 + plone-transformchain==3.0.1
 + plone-uuid==2.0.1
 + plone-z3cform==2.0.5
 + plonetheme-barceloneta==3.3.0
 + pluggy==1.6.0
 + products-btreefolder2==5.1
 + products-cmfcore==3.7
 + products-cmfdifftool==4.0.4
 + products-cmfdynamicviewfti==7.0.2
 + products-cmfeditions==4.0.5
 + products-cmfplacefulworkflow==3.0.5
 + products-cmfplone==6.1.2
 + products-cmfuid==4.2
 + products-daterecurringindex==3.0.1
 + products-dcworkflow==3.0
 + products-extendedpathindex==4.0.1
 + products-externalmethod==6.0
 + products-genericsetup==5.0.0
 + products-isurlinportal==3.0.0
 + products-mailhost==6.0
 + products-mimetypesregistry==3.0.1
 + products-plonepas==8.0.5
 + products-pluggableauthservice==3.0
 + products-pluginregistry==2.0
 + products-portaltransforms==4.1.1
 + products-pythonscripts==5.1
 + products-sessions==6.0
 + products-siteerrorlog==6.0
 + products-standardcachemanagers==5.0
 + products-statusmessages==5.0.6
 + products-zcatalog==7.1
 + products-zopeversioncontrol==4.1
 + prompt-toolkit==3.0.51
 + protobuf==5.29.3
 + pycparser==2.22
 + pygments==2.19.1
 + pyjwt==2.10.1
 + pysocks==1.7.1
 + pytest==8.4.0
 + pytest-cov==6.2.1
 + pytest-plone==1.0.0a2
 + python-dateutil==2.9.0.post0
 + python-gettext==5.0
 + pytz==2024.2
 + record==4.1
 + referencing==0.36.2
 + repoze-xmliter==1.0b1
 + requests==2.32.3
 + restrictedpython==8.0
 + robotframework==6.0.2
 + robotframework-assertion-engine==3.0.3
 + robotframework-browser==19.3.1
 + robotframework-debuglibrary==2.5.0
 + robotframework-pythonlibcore==4.4.1
 + robotframework-selenium2library==3.0.0
 + robotframework-seleniumlibrary==6.1.3
 + robotframework-seleniumtestability==2.1.0
 + robotsuite==2.3.2
 + roman==5.0
 + rpds-py==0.22.3
 + seedir==0.5.0
 + selenium==4.9.1
 + setuptools==80.9.0
 + sgmllib3k==1.0.0
 + simplejson==3.20.1
 + six==1.17.0
 + sniffio==1.3.1
 + sortedcontainers==2.4.0
 + soupsieve==2.6
 + tomli==2.2.1
 + transaction==5.0
 + triangle-notes==1.0.0a0 (from file:///Users/bubba/src/triangle.notes)
 + trio==0.30.0
 + trio-websocket==0.12.2
 + typing-extensions==4.14.0
 + tzdata==2025.2
 + unidecode==1.4.0
 + urllib3==2.3.0
 + waitress==3.0.2
 + wcwidth==0.2.13
 + webob==1.8.9
 + webresource==1.2
 + webtest==3.0.3
 + wrapt==1.17.2
 + wsgiproxy2==0.5.1
 + wsproto==1.2.0
 + z3c-caching==4.0
 + z3c-form==6.0
 + z3c-formwidget-query==3.0
 + z3c-jbot==3.0
 + z3c-objpath==3.0
 + z3c-pt==5.0
 + z3c-relationfield==3.0
 + z3c-unconfigure==3.0
 + z3c-zcmlhook==3.0
 + zc-lockfile==3.0.post1
 + zc-relation==2.1
 + zconfig==4.2
 + zexceptions==5.0
 + zodb==6.0.1
 + zodbpickle==4.1.1
 + zope==5.13
 + zope-annotation==5.0
 + zope-app-locales==5.0
 + zope-browser==3.0
 + zope-browsermenu==5.0
 + zope-browserpage==5.0
 + zope-browserresource==5.1
 + zope-cachedescriptors==5.0
 + zope-component==6.0
 + zope-componentvocabulary==3.0
 + zope-configuration==6.0
 + zope-container==6.1
 + zope-contentprovider==6.0
 + zope-contenttype==5.1
 + zope-copy==5.0
 + zope-datetime==5.0.0
 + zope-deferredimport==5.0
 + zope-deprecation==5.1
 + zope-dottedname==6.0
 + zope-event==5.0
 + zope-exceptions==5.2
 + zope-filerepresentation==6.0
 + zope-globalrequest==2.0
 + zope-hookable==7.0
 + zope-i18n==5.2
 + zope-i18nmessageid==7.0
 + zope-interface==7.2
 + zope-intid==5.1
 + zope-keyreference==6.1
 + zope-lifecycleevent==5.0
 + zope-location==5.0
 + zope-pagetemplate==5.1
 + zope-processlifetime==3.0
 + zope-proxy==6.1
 + zope-ptresource==5.0
 + zope-publisher==7.3
 + zope-pytestlayer==8.3
 + zope-ramcache==3.1
 + zope-schema==7.0.1
 + zope-security==7.3
 + zope-sendmail==6.2
 + zope-sequencesort==5.0
 + zope-site==5.0
 + zope-size==5.0
 + zope-structuredtext==5.0
 + zope-tal==5.0.1
 + zope-tales==6.0
 + zope-testbrowser==7.0
 + zope-testing==5.0.1
 + zope-testrunner==7.2
 + zope-traversing==5.0
 + zope-viewlet==5.0
      Built plonecli==2.5
Installed 26 packages in 84ms

RUN: mrbob bobtemplates.plone:content_type -c .mrbob.ini

Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered.

Answer with a question mark to display help.
Values in square brackets at the end of the questions show the default value if there is no answer.



RUN: git status --porcelain --ignore-submodules
git status result:
----------------------------
b'A  .editorconfig\nA  .flake8\nA  .github/workflows/ci.yml\nA  .gitignore\nA  .mrbob.ini\nA  CHANGELOG.md\nA  CONTRIBUTORS.md\nA  LICENSE.GPL\nA  LICENSE.md\nA  Makefile\nA  README.md\nA  bobtemplate.cfg\nA  instance.yaml\nA  mx.ini\nA  news/.changelog_template.jinja\nA  news/.gitkeep\nA  pyproject.toml\nA  scripts/create_site.py\nA  src/triangle/__init__.py\nA  src/triangle/notes/__init__.py\nA  src/triangle/notes/browser/__init__.py\nA  src/triangle/notes/browser/configure.zcml\nA  src/triangle/notes/browser/overrides/.gitkeep\nA  src/triangle/notes/browser/static/.gitkeep\nA  src/triangle/notes/configure.zcml\nA  src/triangle/notes/content/__init__.py\nA  src/triangle/notes/controlpanels/__init__.py\nA  src/triangle/notes/controlpanels/configure.zcml\nA  src/triangle/notes/dependencies.zcml\nA  src/triangle/notes/indexers/__init__.py\nA  src/triangle/notes/indexers/configure.zcml\nA  src/triangle/notes/interfaces.py\nA  src/triangle/notes/locales/__init__.py\nA  src/triangle/notes/locales/__main__.py\nA  src/triangle/notes/locales/en/LC_MESSAGES/triangle.notes.po\nA  src/triangle/notes/locales/triangle.notes.pot\nA  src/triangle/notes/permissions.zcml\nA  src/triangle/notes/profiles.zcml\nA  src/triangle/notes/profiles/default/browserlayer.xml\nA  src/triangle/notes/profiles/default/catalog.xml\nA  src/triangle/notes/profiles/default/controlpanel.xml\nA  src/triangle/notes/profiles/default/diff_tool.xml\nA  src/triangle/notes/profiles/default/metadata.xml\nA  src/triangle/notes/profiles/default/registry/main.xml\nA  src/triangle/notes/profiles/default/repositorytool.xml\nA  src/triangle/notes/profiles/default/rolemap.xml\nA  src/triangle/notes/profiles/default/types.xml\nA  src/triangle/notes/profiles/default/types/.gitkeep\nA  src/triangle/notes/profiles/uninstall/browserlayer.xml\nA  src/triangle/notes/setuphandlers/__init__.py\nA  src/triangle/notes/testing.py\nA  src/triangle/notes/upgrades/__init__.py\nA  src/triangle/notes/upgrades/configure.zcml\nA  src/triangle/notes/vocabularies/__init__.py\nA  src/triangle/notes/vocabularies/configure.zcml\nA  tests/conftest.py\nA  tests/setup/test_setup_install.py\nA  tests/setup/test_setup_uninstall.py\n?? docs/\n'
--> Please commit your changes, before using a sub-template! Continue anyway? [n/y] [n]: y

--> Content type name (Allowed: _ a-z A-Z and whitespace) [Todo Task]: triangle_note

--> Content type description: Notes for Triangle Manufacturing

--> Content type icon (See https://icons.getbootstrap.com/) [puzzle]:

--> Use XML Model [n]:

--> Dexterity base class (Container/Item) [Container]:

--> Should the content type globally addable? [y]:

--> Should we filter content types to be added to this container? [n]:

--> Create a content type class [y]:

--> Activate default behaviors? [y]:

>>> reading Plone version from bobtemplate.cfg

Traceback (most recent call last):
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/bin/plonecli", line 12, in <module>
    sys.exit(cli())
             ^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/click/core.py", line 1442, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/click/core.py", line 1363, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/click/core.py", line 1861, in invoke
    rv.append(sub_ctx.command.invoke(sub_ctx))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/click/core.py", line 1226, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/click/core.py", line 794, in invoke
    return callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/click/decorators.py", line 34, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/plonecli/cli.py", line 128, in add
    mrbobmain(mrbob_args)
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/mrbob/cli.py", line 175, in main
    c.render()
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/mrbob/configurator.py", line 185, in render
    f(self)
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/bobtemplates/plone/content_type.py", line 270, in prepare_renderer
    dx_type_name_klass = get_normalized_classname(type_name)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bubba/.cache/uv/archive-v0/SkwULtgn14jxMfrLQGHH1/lib/python3.11/site-packages/bobtemplates/plone/base.py", line 541, in get_normalized_classname
    normalized_name = cc.pascalcase(name)
                      ^^^^^^^^^^^^^
AttributeError: module 'case_conversion' has no attribute 'pascalcase'
make: *** [add] Error 1

In case-conversion · PyPI I see a method pascal but none called pascalcase

In case-converter · PyPI I see pascalcase

% uv pip show case-conversion
warning: Package(s) not found for: case-conversion

plonecli is run using uvx: https://github.com/plone/cookieplone-templates/blob/main/templates/add-ons/backend/{{%20cookiecutter.__folder_name%20}}/Makefile#L138

That means that uv will install it into its own virtualenv (similar to pipx) rather than using the same virtualenv as the Plone backend.

Try running uvx -v –-reinstall plonecli and then you should get a detailed log where you can see what it installed.

It looks like this is because there was a new backwards-incompatible 3.0.0 release of case-conversion on August 27. I filed a bug here: Not compatible with case-conversion 3.x · Issue #589 · plone/bobtemplates.plone · GitHub

1 Like

If you use case-conversion 2.1.0 you can get around that error

1 Like

…but then as per Discord there is a discrepancy between the way cookieplone and plonecli expect directories to be set up