diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 72e55526e8c4..d199f40c7948 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -244,6 +244,14 @@ Coding Style - Use `PEP8 `__ style. Not only is this style good for readability in an absolute sense, but consistent styling helps us all read each other's code. +- This includes module names: all modules, including instrument driver + modules, should be named using lower case ``snake_case``, e.g. + ``weinschel_8320.py`` and not ``Weinschel_8320.py``. Vendor and model + capitalization belongs in the class name (``Weinschel8320``), not in the + module name. This is enforced by the ruff rule ``N999`` + (``invalid-module-name``). A number of existing driver modules predate this + rule and are exempted in ``pyproject.toml`` because renaming them would + break user code; new modules should not be added to that exemption list. - There is a command-line tool (``pip install pycodestyle``) you can run after writing code to validate its style. - A lot of editors have plugins that will check this for you diff --git a/docs/changes/newsfragments/8356.improved b/docs/changes/newsfragments/8356.improved new file mode 100644 index 000000000000..3244dfdc7227 --- /dev/null +++ b/docs/changes/newsfragments/8356.improved @@ -0,0 +1,4 @@ +The documentation on naming instrument drivers (in the Contributor guide and in the +"Creating Instrument Drivers" example notebook) has been updated to state that instrument driver +modules should be named using lower case ``snake_case``, e.g. ``weinschel_8320.py``. Vendor and +model capitalization belongs in the class name (``Weinschel8320``) and not in the module name. diff --git a/docs/changes/newsfragments/8356.underthehood b/docs/changes/newsfragments/8356.underthehood new file mode 100644 index 000000000000..b98ca93675b5 --- /dev/null +++ b/docs/changes/newsfragments/8356.underthehood @@ -0,0 +1,3 @@ +Enable the ruff rule ``N999`` (``invalid-module-name``). Instrument driver modules that are part of the +public ``qcodes`` namespace are exempted via ``lint.pep8-naming.extend-ignore-names`` in ``pyproject.toml`` +so that no public module names change. Test modules with invalid names have been renamed to lower case. diff --git a/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb b/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb index 62d7174e64c8..5699f5e638f9 100644 --- a/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb +++ b/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb @@ -124,13 +124,13 @@ "The same rules should apply for QCoDeS-contrib-drivers with the exception that all drivers are stored in subfolders of the drivers folder. \n", "\n", "### Naming the Instrument class\n", - "A driver for an instrument with model `Model` and from the vendor `Vendor` should be stored in the file:\n", + "A driver for an instrument with model `Model` and from the vendor `Vendor` should be stored in the module:\n", "\n", "```\n", - "qcodes\\instrument_drivers\\{Vendor}\\{Vendor}_{Model}.py \n", + "qcodes\\instrument_drivers\\{Vendor}\\{vendor}_{model}.py \n", "```\n", - "using snake case with an underscore between the vendor and model name but starting the\n", - "vendor name with upper case.\n", + "i.e. the module (file) name should be all lower case snake case with an underscore between the\n", + "vendor name and the model name.\n", "\n", "The primary instrument class should be named as follows:\n", "```\n", @@ -139,12 +139,17 @@ "```\n", "E.g Vendor followed by Model number in CamelCase.\n", "\n", - "Note that we use vendor names starting with upper case for both folders and file names.\n", + "Note that we use vendor names starting with upper case for folders but all lower case for module\n", + "(file) names. Module names must be valid lower case snake case identifiers; this is enforced by the\n", + "`N999` (`invalid-module-name`) ruff rule. A number of existing driver modules predate this rule and\n", + "are still named using `Vendor_Model` capitalization. Those are exempted via\n", + "`lint.pep8-naming.extend-ignore-names` in `pyproject.toml`, since renaming them would break user\n", + "code, but no new exemptions should be added.\n", "\n", "It is also fine to use an acronym for instrument vendors when there are well established. E.g. drivers for `American Magnetics Inc.` instruments\n", "may use the acronym `AMI` to refer to the vendor.\n", "\n", - "As an example the driver for the Weinschel 8320 should be stored in the file `qcodes\\instrument_drivers\\Weinschel\\Weinschel_8320.py` and the \n", + "As an example the driver for the Weinschel 8320 should be stored in the module `qcodes\\instrument_drivers\\Weinschel\\weinschel_8320.py` and the \n", "class named `Weinschel8320` \n", "\n", "### Naming InstrumentModule classes\n", @@ -161,11 +166,11 @@ "\n", "As an example have a look at the Keysight 344xxA series of digital multi meters. To implement drivers for such instruments it is preferable\n", "to implement a private base class such as `_Keysight344xxA`. This class should be stored either in a `private` subfolder of the Vendor folder or\n", - "in a file starting with an underscore i.e. `_Keysight344xxA.py`. If possible, we prefer a format where `x` is used to signal the parts of the model numbers that \n", + "in a module starting with an underscore i.e. `_keysight_344xxa.py`. If possible, we prefer a format where `x` is used to signal the parts of the model numbers that \n", "may change. Along with this class subclasses for each of the supported models should be implemented. These may either make small modifications to the baseclass as needed \n", "or be empty subclasses if no modifications are needed. \n", "\n", - "E.g. subclasses of the Keysight 344xxA driver for the specific model `34410A` should be named as `Keysight34410A` and stored in `Keysight34410A.py`.\n", + "E.g. subclasses of the Keysight 344xxA driver for the specific model `34410A` should be named as `Keysight34410A` and stored in `keysight_34410a.py`.\n", "\n" ] }, diff --git a/pyproject.toml b/pyproject.toml index 0ff7239f30e9..acdd6757b1ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -258,7 +258,7 @@ extend-select = [ # it may be worth fixing some or these in the future # PYI036 disable until https://github.com/astral-sh/ruff/issues/9794 is fixed ignore = ["E501", "G004", "PLR2004", "PLR0913", "PLR0911", "PLR0912", "PLR0915", "PLW0602", "PLW0603", "PLW2901", "PYI036", "PLR0917", - "N999", "BLE001", "TRY004", "TRY002", "S110"] # new defaults of 0.16.0 that we don't want to enable yet + "BLE001", "TRY004", "TRY002", "S110"] # new defaults of 0.16.0 that we don't want to enable yet # we want to explicitly use the micro symbol # not the greek letter @@ -283,6 +283,158 @@ banned-module-level-imports = [ "xarray", "cf_xarray","pandas", "opencensus", "tqdm.dask", "dask", "matplotlib", "IPython", "ruamel", "tabulate", "h5netcdf", "PIL", "qcodes_loop"] + +[tool.ruff.lint.pep8-naming] +# N999 (invalid-module-name): these instrument driver modules are part of +# the public qcodes namespace so they cannot be renamed to lower case +# without breaking user code. New modules should use lower case names. +extend-ignore-names = [ + "_Agilent_344xxA", + "_AimTTi_PL_P", + "_Keithley_2600", + "_Keysight_N5232B", + "_M5065", + "_M5180", + "_M5xxx", + "_TM620", + "Agilent_34401A", + "Agilent_34410A", + "Agilent_34411A", + "Agilent_E8257D", + "Agilent_E8267C", + "Aim_TTi_PL068_P", + "Aim_TTi_PL155_P", + "Aim_TTi_PL303_P", + "Aim_TTi_PL303QMD_P", + "Aim_TTi_PL303QMT_P", + "Aim_TTi_PL601_P", + "Aim_TTi_QL355_TP", + "AMI430_visa", + "ATS", + "ATS_acquisition_controllers", + "ATS9360", + "ATS9373", + "ATS9440", + "ATS9870", + "AWG5014", + "AWG5208", + "AWG70000A", + "AWG70002A", + "AWGFileParser", + "Base_SPDT", + "BaselSP983", + "BaselSP983a", + "BaselSP983c", + "Decadac", + "DP8xx", + "DPO7200xx", + "DynaCool", + "HP_8133A", + "HP_83650A", + "HP_8753D", + "Infiniium", + "Ithaco_1211", + "Keithley_2000", + "Keithley_2400", + "Keithley_2450", + "Keithley_2601B", + "Keithley_2602A", + "Keithley_2602B", + "Keithley_2604B", + "Keithley_2611B", + "Keithley_2612B", + "Keithley_2614B", + "Keithley_2634B", + "Keithley_2635B", + "Keithley_2636B", + "Keithley_3706A", + "Keithley_6500", + "Keithley_7510", + "Keithley_s46", + "Keysight_33210a", + "Keysight_33250a", + "Keysight_33510b", + "Keysight_33511b", + "Keysight_33512b", + "Keysight_33521b", + "Keysight_33522b", + "Keysight_33611a", + "Keysight_33612a", + "Keysight_33621a", + "Keysight_33622a", + "Keysight_34410A_submodules", + "Keysight_34411A_submodules", + "Keysight_34460A_submodules", + "Keysight_34461A_submodules", + "Keysight_34465A_submodules", + "Keysight_34470A_submodules", + "Keysight_344xxA_submodules", + "Keysight_B2962A", + "Keysight_N5173B", + "Keysight_N5183B", + "Keysight_N5222B", + "Keysight_N5230C", + "Keysight_N5245A", + "Keysight_N6705B", + "Keysight_N9030B", + "Keysight_P5002B", + "Keysight_P5004B", + "Keysight_P9374A", + "KeysightAgilent_33XXX", + "KeysightB1500_base", + "KeysightB1500_module", + "KeysightB1500_sampling_measurement", + "KeysightB1511B", + "KeysightB1517A", + "KeysightB1520A", + "KeysightB1530A", + "KtM960x", + "KtM960xDefs", + "KtMAwg", + "KtMAwgDefs", + "Lakeshore_model_325", + "Lakeshore_model_336", + "Lakeshore_model_372", + "MercuryiPS_VISA", + "N51x1", + "N52xx", + "QDac_channels", + "Rigol_DG1062", + "Rigol_DG4000", + "Rigol_DP821", + "Rigol_DP831", + "Rigol_DP832", + "Rigol_DS1074Z", + "Rigol_DS4000", + "Rohde_Schwarz_ZNB20", + "Rohde_Schwarz_ZNB8", + "RTO1000", + "SG384", + "SGS100A", + "SignalHound_USB_SA124B", + "SR560", + "SR830", + "SR860", + "SR865", + "SR865A", + "SR86x", + "Tektronix_70001A", + "Tektronix_70001B", + "Tektronix_70002B", + "Tektronix_DPO5000", + "Tektronix_DPO7000", + "Tektronix_DPO70000", + "Tektronix_DSA70000", + "Tektronix_MSO5000", + "Tektronix_MSO70000", + "TPS2012", + "USBHIDMixin", + "Weinschel_8320", + "Yokogawa_GS200", + "ZNB", + "ZNB20", +] + [tool.ruff.lint.pydocstyle] convention = "google" diff --git a/tests/dataset/dond/test_doNd.py b/tests/dataset/dond/test_dond.py similarity index 100% rename from tests/dataset/dond/test_doNd.py rename to tests/dataset/dond/test_dond.py diff --git a/tests/drivers/keysight_b1500/test_commandList.py b/tests/drivers/keysight_b1500/test_commandlist.py similarity index 100% rename from tests/drivers/keysight_b1500/test_commandList.py rename to tests/drivers/keysight_b1500/test_commandlist.py diff --git a/tests/drivers/keysight_b1500/test_MessageBuilder.py b/tests/drivers/keysight_b1500/test_messagebuilder.py similarity index 100% rename from tests/drivers/keysight_b1500/test_MessageBuilder.py rename to tests/drivers/keysight_b1500/test_messagebuilder.py diff --git a/tests/drivers/test_Agilent_E8257D.py b/tests/drivers/test_agilent_e8257d.py similarity index 100% rename from tests/drivers/test_Agilent_E8257D.py rename to tests/drivers/test_agilent_e8257d.py diff --git a/tests/drivers/test_AimTTi_PL601P.py b/tests/drivers/test_aimtti_pl601p.py similarity index 100% rename from tests/drivers/test_AimTTi_PL601P.py rename to tests/drivers/test_aimtti_pl601p.py diff --git a/tests/drivers/test_CopperMountain_M5065.py b/tests/drivers/test_coppermountain_m5065.py similarity index 100% rename from tests/drivers/test_CopperMountain_M5065.py rename to tests/drivers/test_coppermountain_m5065.py diff --git a/tests/drivers/test_CopperMountain_M5180.py b/tests/drivers/test_coppermountain_m5180.py similarity index 100% rename from tests/drivers/test_CopperMountain_M5180.py rename to tests/drivers/test_coppermountain_m5180.py diff --git a/tests/drivers/test_cryomagnetics_TM620.py b/tests/drivers/test_cryomagnetics_tm620.py similarity index 100% rename from tests/drivers/test_cryomagnetics_TM620.py rename to tests/drivers/test_cryomagnetics_tm620.py diff --git a/tests/drivers/test_Keithley_2450.py b/tests/drivers/test_keithley_2450.py similarity index 100% rename from tests/drivers/test_Keithley_2450.py rename to tests/drivers/test_keithley_2450.py diff --git a/tests/drivers/test_keithley_3706A.py b/tests/drivers/test_keithley_3706a.py similarity index 100% rename from tests/drivers/test_keithley_3706A.py rename to tests/drivers/test_keithley_3706a.py diff --git a/tests/drivers/test_Keysight_33XXX.py b/tests/drivers/test_keysight_33xxx.py similarity index 100% rename from tests/drivers/test_Keysight_33XXX.py rename to tests/drivers/test_keysight_33xxx.py diff --git a/tests/drivers/test_Keysight_N6705B.py b/tests/drivers/test_keysight_n6705b.py similarity index 100% rename from tests/drivers/test_Keysight_N6705B.py rename to tests/drivers/test_keysight_n6705b.py diff --git a/tests/drivers/test_MercuryiPS.py b/tests/drivers/test_mercuryips.py similarity index 100% rename from tests/drivers/test_MercuryiPS.py rename to tests/drivers/test_mercuryips.py diff --git a/tests/drivers/test_Rigol_DS1074Z.py b/tests/drivers/test_rigol_ds1074z.py similarity index 100% rename from tests/drivers/test_Rigol_DS1074Z.py rename to tests/drivers/test_rigol_ds1074z.py diff --git a/tests/drivers/test_RS_SGS100A.py b/tests/drivers/test_rs_sgs100a.py similarity index 100% rename from tests/drivers/test_RS_SGS100A.py rename to tests/drivers/test_rs_sgs100a.py diff --git a/tests/drivers/test_tektronix_AWG5014C.py b/tests/drivers/test_tektronix_awg5014c.py similarity index 100% rename from tests/drivers/test_tektronix_AWG5014C.py rename to tests/drivers/test_tektronix_awg5014c.py diff --git a/tests/drivers/test_tektronix_AWG5208.py b/tests/drivers/test_tektronix_awg5208.py similarity index 100% rename from tests/drivers/test_tektronix_AWG5208.py rename to tests/drivers/test_tektronix_awg5208.py diff --git a/tests/drivers/test_tektronix_AWG70000A.py b/tests/drivers/test_tektronix_awg70000a.py similarity index 100% rename from tests/drivers/test_tektronix_AWG70000A.py rename to tests/drivers/test_tektronix_awg70000a.py