diff --git a/dependencies/normalizers/simulation/band_structure b/dependencies/normalizers/simulation/band_structure index 95f42793d8c8babb5b3503ef2b63e3d403a24e4e..9f52ea64d13a0fbc38d40608b34c6b5870fac6b0 160000 --- a/dependencies/normalizers/simulation/band_structure +++ b/dependencies/normalizers/simulation/band_structure @@ -1 +1 @@ -Subproject commit 95f42793d8c8babb5b3503ef2b63e3d403a24e4e +Subproject commit 9f52ea64d13a0fbc38d40608b34c6b5870fac6b0 diff --git a/dependencies/normalizers/simulation/dos b/dependencies/normalizers/simulation/dos index e47742260dc747b3a8ad00d3a3d5dbe223c125ba..545285f9826a1092015ac9283feafeb04bcbaaf4 160000 --- a/dependencies/normalizers/simulation/dos +++ b/dependencies/normalizers/simulation/dos @@ -1 +1 @@ -Subproject commit e47742260dc747b3a8ad00d3a3d5dbe223c125ba +Subproject commit 545285f9826a1092015ac9283feafeb04bcbaaf4 diff --git a/dependencies/normalizers/simulation/soap b/dependencies/normalizers/simulation/soap index 06800da2a4c6634dd843d1b66ae5cd5eae9ac9ad..bf413d1c6fcf322b91dd8ca71679ef97e22cc5bd 160000 --- a/dependencies/normalizers/simulation/soap +++ b/dependencies/normalizers/simulation/soap @@ -1 +1 @@ -Subproject commit 06800da2a4c6634dd843d1b66ae5cd5eae9ac9ad +Subproject commit bf413d1c6fcf322b91dd8ca71679ef97e22cc5bd diff --git a/dependencies/normalizers/simulation/spectra b/dependencies/normalizers/simulation/spectra index 6e05a3b042c44c86b01cb6b635856c6d49b740f5..95e885fe74aec6b6ef57c255d5eacee1e2728a42 160000 --- a/dependencies/normalizers/simulation/spectra +++ b/dependencies/normalizers/simulation/spectra @@ -1 +1 @@ -Subproject commit 6e05a3b042c44c86b01cb6b635856c6d49b740f5 +Subproject commit 95e885fe74aec6b6ef57c255d5eacee1e2728a42 diff --git a/dependencies/normalizers/simulation/system b/dependencies/normalizers/simulation/system index 7d078267f4a644e7c4ea87740669ea362c60775d..5ed4b3af700dff35e5717cffdf67c8d66df49519 160000 --- a/dependencies/normalizers/simulation/system +++ b/dependencies/normalizers/simulation/system @@ -1 +1 @@ -Subproject commit 7d078267f4a644e7c4ea87740669ea362c60775d +Subproject commit 5ed4b3af700dff35e5717cffdf67c8d66df49519 diff --git a/dependencies/normalizers/simulation/workflow b/dependencies/normalizers/simulation/workflow index 5ac17eba4a206112753a1fab7c7d548759492d59..dd5f1d2a2c48ed81d509a1a94ba41b18daa8a922 160000 --- a/dependencies/normalizers/simulation/workflow +++ b/dependencies/normalizers/simulation/workflow @@ -1 +1 @@ -Subproject commit 5ac17eba4a206112753a1fab7c7d548759492d59 +Subproject commit dd5f1d2a2c48ed81d509a1a94ba41b18daa8a922 diff --git a/gui/src/components/About.js b/gui/src/components/About.js index 4cb4d3284273d4db83219947ce3b225b47f9540e..38e61b75346d052596c1def4dd00046fef1d7b2b 100644 --- a/gui/src/components/About.js +++ b/gui/src/components/About.js @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useLayoutEffect, useRef, useCallback, useEffect, useState, useMemo } from 'react' +import React, { useLayoutEffect, useRef, useCallback, useEffect, useState } from 'react' import { ReactComponent as AboutSvg } from '../images/about.svg' import PropTypes from 'prop-types' import Markdown from './Markdown' @@ -35,114 +35,9 @@ import { Typography } from '@material-ui/core' import { Link as RouterLink, useHistory } from 'react-router-dom' -import InputConfig from './search/input/InputConfig' import { useInfo } from './api' import { pluralize } from '../utils' -/** - * Displays an info dialog. - */ -function InfoDialog({title, data, DialogProps, onClose}) { - if (!data) return null - - return <Dialog maxWidth='md' fullWidth open={true} {...DialogProps}> - <DialogTitle>{title}</DialogTitle> - <DialogContent> - <InputConfig data={data} format="YAML" readOnly/> - </DialogContent> - <DialogActions> - <Button onClick={() => onClose?.()} color="primary"> - close - </Button> - </DialogActions> - </Dialog> -} -InfoDialog.propTypes = { - title: PropTypes.string, - data: PropTypes.object, - DialogProps: PropTypes.object, - onClose: PropTypes.func -} - -/** - * Displays a list of information about the version, plugin package and plugin - * entry points. - */ -export const DistributionInfo = React.memo(({data}) => { - const [selected, setSelected] = useState() - const [title, setTitle] = useState() - - const categories = useMemo(() => { - if (!data) return {} - - const categories = {} - data.plugin_entry_points - .forEach((entryPoint) => { - let category = entryPoint.entry_point_type || entryPoint.plugin_type - // TODO: Schema plugins are recategorized as package plugins. This can be - // removed once all plugins have been migrated and we remove the old plugin - // models. - if (category === 'schema') { - category = 'schema_package' - } - category = category.replace('_', ' ') - if (categories[category]) { - categories[category].push(entryPoint) - } else { - categories[category] = [entryPoint] - } - }) - - Object.keys(categories).forEach(category => { - categories[category] = categories[category].sort((a, b) => { - const nameA = a.name - const nameB = b.name - return (nameA > nameB) ? 1 : -1 - }) - }) - - return categories - }, [data]) - - return data - ? <ul> - <li>version: {data.version}</li> - {data.plugin_packages - ? <li>{"plugin packages: "} - {data.plugin_packages.map(pluginPackage => <> - <Link key={pluginPackage.name} href="#" onClick={() => { - setSelected(pluginPackage) - setTitle('Plugin package') - }}>{pluginPackage.name}</Link> - {", "} - </>)} - </li> - : null - } - {Object.keys(categories) - .sort() - .map((category) => { - const entryPoints = categories[category] - return <li key={category}> - {`${pluralize(category, 2)}: `} - {entryPoints.map(entryPoint => <> - <Link key={entryPoint.id} href="#" onClick={() => { - setSelected(entryPoint) - setTitle('Plugin entry point') - }}>{entryPoint.id}</Link> - {", "} - </>)} - </li> - })} - <InfoDialog title={title} data={selected} onClose={() => setSelected(null)} /> - </ul> - : 'Loading...' -}) - -DistributionInfo.propTypes = { - data: PropTypes.object -} - function CodeInfo({code, ...props}) { if (!code) { return null @@ -312,9 +207,6 @@ const useStyles = makeStyles(theme => ({ maxWidth: 1024, margin: 'auto', width: '100%' - }, - header: { - margin: '24px 0px' } })) @@ -524,12 +416,13 @@ export default function About() { with password \`password\`. The user \`sheldon.cooper@nomad-fairdi.tests.de\` is used for data that has no provenance with the original NOMAD CoE database. ` : ''} + + ### About this version + - version: \`${info ? info.version : 'loading'}\` + - parsers: ${info ? info.parsers.join(', ') : 'loading'} + - normalizers: ${info ? info.normalizers.join(', ') : 'loading'} `}</Markdown> </Grid> - <Grid item xs={12}> - <Typography variant='h5' className={classes.header}>About this distribution</Typography> - <DistributionInfo data={info} /> - </Grid> </Grid> </div> } diff --git a/gui/src/components/nav/Routes.js b/gui/src/components/nav/Routes.js index 2f2af38223feef9e390fcbff910bae24d56d92cd..abc69391b05cc4697850ed73b268f3851462e9e3 100644 --- a/gui/src/components/nav/Routes.js +++ b/gui/src/components/nav/Routes.js @@ -35,7 +35,7 @@ import APIs from '../APIs' import SearchPage from '../search/SearchPage' import { SearchContext } from '../search/SearchContext' import NorthPage, {help as NORTHHelp} from '../north/NorthPage' -import { aitoolkitEnabled, appBase, oasis, encyclopediaBase, ui, apps } from '../../config' +import { aitoolkitEnabled, appBase, oasis, encyclopediaBase, ui } from '../../config' import EntryQuery from '../entry/EntryQuery' import ResolvePID from '../entry/ResolvePID' import DatasetPage, { help as datasetHelp } from '../dataset/DatasetPage' @@ -181,7 +181,7 @@ const toolkitRoute = (!oasis && aitoolkitEnabled) tooltip: 'Visit the NOMAD Artificial Intelligence Analytics Toolkit' } -const searchRoutes = apps +const searchRoutes = Object.values(ui?.apps?.options || {}) .map((context) => { const routeMap = { entries: entryRoutes diff --git a/gui/src/components/search/input/InputConfig.js b/gui/src/components/search/input/InputConfig.js index 8fa80536428341c93156edc30ec57c3f229c1918..c151c25c6cce6773e623c6f8a157a004aa204a09 100644 --- a/gui/src/components/search/input/InputConfig.js +++ b/gui/src/components/search/input/InputConfig.js @@ -25,7 +25,7 @@ import YAML from 'yaml' /** * Form used for editing a YAML or JSON config. */ -const InputConfig = React.memo(({data, format, maxRows, minRows, onChange, error, onError, readOnly}) => { +const InputConfig = React.memo(({data, format, maxRows, minRows, onChange, error, onError}) => { const controlledError = useRef(error !== undefined) const [serialized, setSerialized] = useState() const [errorInternal, setErrorInternal] = useState() @@ -78,9 +78,6 @@ const InputConfig = React.memo(({data, format, maxRows, minRows, onChange, error minRows={minRows} value={serialized} onChange={handleChange} - InputProps={{ - readOnly: readOnly - }} /> ) }) @@ -91,8 +88,7 @@ InputConfig.propTypes = { minRows: PropTypes.number, onChange: PropTypes.func, error: PropTypes.string, - onError: PropTypes.func, - readOnly: PropTypes.bool + onError: PropTypes.func } InputConfig.defaultProps = { diff --git a/gui/src/config.js b/gui/src/config.js index 05fc3302f37238ab9ca8b715682ad50a4b33f87a..9219bc27b62552e78c600f033589f0c1807d4348 100644 --- a/gui/src/config.js +++ b/gui/src/config.js @@ -88,13 +88,6 @@ export const apiBase = `${appBase}/api` export const northBase = urlAbs(window.nomadEnv.northBase) export const guiBase = process.env.PUBLIC_URL export const ui = normalizeConfig(window.nomadEnv.ui) -export const entry_points = normalizeConfig(window.nomadEnv?.plugins?.entry_points) -export const apps = Object.values(ui?.apps?.options || []) -Object.values(entry_points?.options || []) - .filter(entry_point => entry_point.entry_point_type === 'app') - .forEach(entry_point => { - apps.push(entry_point.app) - }) export const servicesUploadLimit = window.nomadEnv.servicesUploadLimit export const keycloakBase = window.nomadEnv.keycloakBase export const keycloakRealm = window.nomadEnv.keycloakRealm diff --git a/gui/src/utils.js b/gui/src/utils.js index 9b3b46cb7b5d14aafeb1138de96f9b519cb4361e..c4305bfbebd2e40985616962a758942c342a0e52 100644 --- a/gui/src/utils.js +++ b/gui/src/utils.js @@ -825,11 +825,7 @@ export function pluralize(word, count, inclusive, format = true, prefix) { 'mainfile': 'mainfiles', 'folder': 'folders', 'has': 'have', - 'activity': 'activities', - 'parser': 'parsers', - 'normalizer': 'normalizers', - 'app': 'apps', - 'package': 'packages' + 'activity': 'activities' } const words = word.trim().split(" ") let lastWord = words[words.length - 1] diff --git a/gui/tests/env.js b/gui/tests/env.js index 65e35dc5bf93a41821aa50fc06f558eebab0c203..fd78f4ce95adbe6f8f68c6c35592e58aa0c657a5 100644 --- a/gui/tests/env.js +++ b/gui/tests/env.js @@ -3810,495 +3810,5 @@ window.nomadEnv = { "enabled": true }, "example_uploads": {} - }, - "plugins": { - "entry_points": { - "include": [ - "schema/simulation/run", - "schema/simulation/workflow", - "parsers/vasp" - ], - "exclude": [], - "options": { - "normalizers/simulation/band_structure": { - "plugin_type": "normalizer", - "id": "normalizers/simulation/band_structure", - "name": "bandstructurenormalizer", - "description": "This is the normalizer for band structure in NOMAD.\n" - }, - "normalizers/simulation/dos": { - "plugin_type": "normalizer", - "id": "normalizers/simulation/dos", - "name": "dosnormalizer", - "description": "This is the normalizer for DOS in NOMAD.\n" - }, - "normalizers/simulation/soap": { - "plugin_type": "normalizer", - "id": "normalizers/simulation/soap", - "name": "soapnormalizer", - "description": "This is the normalizer for SOAP in NOMAD.\n" - }, - "normalizers/simulation/spectra": { - "plugin_type": "normalizer", - "id": "normalizers/simulation/spectra", - "name": "spectranormalizer", - "description": "This is the normalizer for spectra in NOMAD.\n" - }, - "normalizers/simulation/system": { - "plugin_type": "normalizer", - "id": "normalizers/simulation/system", - "name": "systemnormalizer", - "description": "This is the normalizer for system in NOMAD.\n" - }, - "normalizers/simulation/workflow": { - "plugin_type": "normalizer", - "id": "normalizers/simulation/workflow", - "name": "simulationworkflownormalizer", - "description": "This is the normalizer for simulation workflows in NOMAD.\n" - }, - "parsers/abacus": { - "plugin_type": "parser", - "id": "parsers/abacus", - "name": "parsers/abacus", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/abacus" - }, - "parsers/abinit": { - "plugin_type": "parser", - "id": "parsers/abinit", - "name": "parsers/abinit", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/abinit" - }, - "parsers/aflow": { - "plugin_type": "parser", - "id": "parsers/aflow", - "name": "parsers/aflow", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/aflow" - }, - "parsers/amber": { - "plugin_type": "parser", - "id": "parsers/amber", - "name": "parsers/amber", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/amber" - }, - "parsers/ams": { - "plugin_type": "parser", - "id": "parsers/ams", - "name": "parsers/ams", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/ams" - }, - "parsers/asap": { - "plugin_type": "parser", - "id": "parsers/asap", - "name": "parsers/asap", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/asap" - }, - "parsers/asr": { - "plugin_type": "parser", - "id": "parsers/asr", - "name": "parsers/asr", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/asr" - }, - "parsers/atk": { - "plugin_type": "parser", - "id": "parsers/atk", - "name": "parsers/atk", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/atk" - }, - "parsers/atomate": { - "plugin_type": "parser", - "id": "parsers/atomate", - "name": "parsers/atomate", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/automate" - }, - "parsers/bigdft": { - "plugin_type": "parser", - "id": "parsers/bigdft", - "name": "parsers/bigdft", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/bigdft" - }, - "parsers/bopfox": { - "plugin_type": "parser", - "id": "parsers/bopfox", - "name": "parsers/bopfox", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/bobfox" - }, - "parsers/castep": { - "plugin_type": "parser", - "id": "parsers/castep", - "name": "parsers/castep", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/castep" - }, - "parsers/charmm": { - "plugin_type": "parser", - "id": "parsers/charmm", - "name": "parsers/charmm", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/charmm" - }, - "parsers/chemotion/chemotion": { - "plugin_type": "parser", - "id": "parsers/chemotion/chemotion", - "name": "parsers/chemotion" - }, - "parsers/cp2k": { - "plugin_type": "parser", - "id": "parsers/cp2k", - "name": "parsers/cp2k", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/cp2k" - }, - "parsers/cpmd": { - "plugin_type": "parser", - "id": "parsers/cpmd", - "name": "parsers/cpmd", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/cpmd" - }, - "parsers/crystal": { - "plugin_type": "parser", - "id": "parsers/crystal", - "name": "parsers/crystal", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/crystal" - }, - "parsers/dftbplus": { - "plugin_type": "parser", - "id": "parsers/dftbplus", - "name": "parsers/dftbplus", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/dftplus" - }, - "parsers/dlpoly": { - "plugin_type": "parser", - "id": "parsers/dlpoly", - "name": "parsers/dl-poly", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/dlpoly" - }, - "parsers/dmol3": { - "plugin_type": "parser", - "id": "parsers/dmol3", - "name": "parsers/dmol", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/dmol3" - }, - "parsers/edmft": { - "plugin_type": "parser", - "id": "parsers/edmft", - "name": "parsers/edmft", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/edmft" - }, - "parsers/eelsdbparser": { - "plugin_type": "parser", - "id": "parsers/eelsdbparser", - "name": "parsers/eels", - "plugin_source_code_url": "https://github.com/nomad-coe/nomad-parser-eelsdb" - }, - "parsers/elabftw/elabftw": { - "plugin_type": "parser", - "id": "parsers/elabftw/elabftw", - "name": "parsers/elabftw", - "plugin_source_code_url": "https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR/-/tree/develop/nomad/parsing/elabftw" - }, - "parsers/elastic": { - "plugin_type": "parser", - "id": "parsers/elastic", - "name": "parsers/elastic", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/elastic" - }, - "parsers/elk": { - "plugin_type": "parser", - "id": "parsers/elk", - "name": "parsers/elk", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/elk" - }, - "parsers/exciting": { - "plugin_type": "parser", - "id": "parsers/exciting", - "name": "parsers/exciting", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/exciting" - }, - "parsers/fhi-aims": { - "plugin_type": "parser", - "id": "parsers/fhi-aims", - "name": "parsers/fhi-aims", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/fhiaims" - }, - "parsers/fhivibes": { - "plugin_type": "parser", - "id": "parsers/fhivibes", - "name": "parsers/fhi-vibes", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/fhivibes" - }, - "parsers/fleur": { - "plugin_type": "parser", - "id": "parsers/fleur", - "name": "parsers/fleur", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/fleur" - }, - "parsers/fplo": { - "plugin_type": "parser", - "id": "parsers/fplo", - "name": "parsers/fplo", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/fplo" - }, - "parsers/gamess": { - "plugin_type": "parser", - "id": "parsers/gamess", - "name": "parsers/gamess", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/gamess" - }, - "parsers/gaussian": { - "plugin_type": "parser", - "id": "parsers/gaussian", - "name": "parsers/gaussian", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/gaussian" - }, - "parsers/gpaw": { - "plugin_type": "parser", - "id": "parsers/gpaw", - "name": "parsers/gpaw", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/gpaw" - }, - "parsers/gromacs": { - "plugin_type": "parser", - "id": "parsers/gromacs", - "name": "parsers/gromacs", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/gromacs" - }, - "parsers/gromos": { - "plugin_type": "parser", - "id": "parsers/gromos", - "name": "parsers/gromos", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/gromos" - }, - "parsers/gulp": { - "plugin_type": "parser", - "id": "parsers/gulp", - "name": "parsers/gulp", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/gulp" - }, - "parsers/h5md": { - "plugin_type": "parser", - "id": "parsers/h5md", - "name": "parsers/h5md" - }, - "parsers/lammps": { - "plugin_type": "parser", - "id": "parsers/lammps", - "name": "parsers/lammps", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/lammps" - }, - "parsers/libatoms": { - "plugin_type": "parser", - "id": "parsers/libatoms", - "name": "parsers/lib-atoms", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/libatoms" - }, - "parsers/lobster": { - "plugin_type": "parser", - "id": "parsers/lobster", - "name": "parsers/lobster", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/lobster" - }, - "parsers/magres": { - "plugin_type": "parser", - "id": "parsers/magres", - "name": "parsers/magres", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/magres" - }, - "parsers/molcas": { - "plugin_type": "parser", - "id": "parsers/molcas", - "name": "parsers/molcas", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/molcas" - }, - "parsers/mopac": { - "plugin_type": "parser", - "id": "parsers/mopac", - "name": "parsers/mopac", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/mopac" - }, - "parsers/namd": { - "plugin_type": "parser", - "id": "parsers/namd", - "name": "parsers/namd", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/namd" - }, - "parsers/nexus": { - "plugin_type": "parser", - "id": "parsers/nexus", - "name": "parsers/nexus", - "plugin_source_code_url": "https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR/-/tree/develop/nomad/parsing/nexus" - }, - "parsers/nwchem": { - "plugin_type": "parser", - "id": "parsers/nwchem", - "name": "parsers/nwchem", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/nwchem" - }, - "parsers/ocean": { - "plugin_type": "parser", - "id": "parsers/ocean", - "name": "parsers/ocean", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/ocean" - }, - "parsers/octopus": { - "plugin_type": "parser", - "id": "parsers/octopus", - "name": "parsers/octopus", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/octopus" - }, - "parsers/onetep": { - "plugin_type": "parser", - "id": "parsers/onetep", - "name": "parsers/onetep", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/onetep" - }, - "parsers/openkim": { - "plugin_type": "parser", - "id": "parsers/openkim", - "name": "parsers/openkim", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/databaseparsers/openkim" - }, - "parsers/openmx": { - "plugin_type": "parser", - "id": "parsers/openmx", - "name": "parsers/openmx", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/openmx" - }, - "parsers/orca": { - "plugin_type": "parser", - "id": "parsers/orca", - "name": "parsers/orca", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/orca" - }, - "parsers/phonopy": { - "plugin_type": "parser", - "id": "parsers/phonopy", - "name": "parsers/phonopy", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/phonopy" - }, - "parsers/psi4": { - "plugin_type": "parser", - "id": "parsers/psi4", - "name": "parsers/psi4", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/psi4" - }, - "parsers/qball": { - "plugin_type": "parser", - "id": "parsers/qball", - "name": "parsers/qball", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/qball" - }, - "parsers/qbox": { - "plugin_type": "parser", - "id": "parsers/qbox", - "name": "parsers/qbox", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/qbox" - }, - "parsers/quantum_espresso_epw": { - "plugin_type": "parser", - "id": "parsers/quantum_espresso_epw", - "name": "parsers/quantumespressoepw", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/quantum_espresso_epw" - }, - "parsers/quantum_espresso_phonon": { - "plugin_type": "parser", - "id": "parsers/quantum_espresso_phonon", - "name": "parsers/quantumespressophonon", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/quantum_espresso_phonon" - }, - "parsers/quantum_espresso_xspectra": { - "plugin_type": "parser", - "id": "parsers/quantum_espresso_xspectra", - "name": "parsers/quantumespressoxspectra", - "plugin_source_code_url": "https://github.com/nomad-coe/workflow-parsers/tree/master/workflowparsers/quantum_espresso_xpectra" - }, - "parsers/quantumespresso": { - "plugin_type": "parser", - "id": "parsers/quantumespresso", - "name": "parsers/quantumespresso", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/quantumespresso" - }, - "parsers/siesta": { - "plugin_type": "parser", - "id": "parsers/siesta", - "name": "parsers/siesta", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/siesta" - }, - "parsers/soliddmft": { - "plugin_type": "parser", - "id": "parsers/soliddmft", - "name": "parsers/soliddmft", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/soliddmft" - }, - "parsers/tbstudio": { - "plugin_type": "parser", - "id": "parsers/tbstudio", - "name": "parsers/tbstudio" - }, - "parsers/tinker": { - "plugin_type": "parser", - "id": "parsers/tinker", - "name": "parsers/tinker", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/tinker" - }, - "parsers/turbomole": { - "plugin_type": "parser", - "id": "parsers/turbomole", - "name": "parsers/turbomole", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/turbomole" - }, - "parsers/vasp": { - "plugin_type": "parser", - "id": "parsers/vasp", - "name": "parsers/vasp", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/vasp" - }, - "parsers/w2dynamics": { - "plugin_type": "parser", - "id": "parsers/w2dynamics", - "name": "parsers/w2dynamics", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/w2dynamics" - }, - "parsers/wannier90": { - "plugin_type": "parser", - "id": "parsers/wannier90", - "name": "parsers/wannier90", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/wannier90" - }, - "parsers/wien2k": { - "plugin_type": "parser", - "id": "parsers/wien2k", - "name": "parsers/wien2k", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/wien2k" - }, - "parsers/xtb": { - "plugin_type": "parser", - "id": "parsers/xtb", - "name": "parsers/xtb", - "plugin_source_code_url": "https://github.com/nomad-coe/atomistic-parsers/tree/develop/atomisticparsers/xtb" - }, - "parsers/yambo": { - "plugin_type": "parser", - "id": "parsers/yambo", - "name": "parsers/yambo", - "plugin_source_code_url": "https://github.com/nomad-coe/electronic-parsers/tree/develop/electronicparsers/yambo" - }, - "schema/nomad-perovskite-solar-cells-database/perovskite_solar_cell_database": { - "plugin_type": "schema", - "id": "schema/nomad-perovskite-solar-cells-database/perovskite_solar_cell_database", - "name": "perovskite_solar_cell_database", - "description": "A NOMAD plugin containing the schema for the Perovskite Solar Cell Database." - }, - "schema/simulation/run": { - "plugin_type": "schema", - "id": "schema/simulation/run", - "name": "runschema", - "description": "Run schema plugin for NOMAD.\n" - }, - "schema/simulation/workflow": { - "plugin_type": "schema", - "id": "schema/simulation/workflow", - "name": "simulationworkflowschema", - "description": "This is a collection of schemas for various types of simulation workflows.\n" - } - } - }, - "plugin_packages": {} } } diff --git a/nomad/app/v1/routers/info.py b/nomad/app/v1/routers/info.py index a44825ecc92ce1d070f717939266b1325b3c34cc..2ded9bafd99323954d4fe2440727462763a064ed 100644 --- a/nomad/app/v1/routers/info.py +++ b/nomad/app/v1/routers/info.py @@ -89,14 +89,6 @@ class InfoModel(BaseModel): metainfo_packages: List[str] codes: List[CodeInfoModel] normalizers: List[str] - plugin_entry_points: List[dict] = Field( - None, - desciption='List of plugin entry points that are activated in this deployment.', - ) - plugin_packages: List[dict] = Field( - None, - desciption='List of plugin packages that are installed in this deployment.', - ) statistics: StatisticsModel = Field(None, description='General NOMAD statistics') search_quantities: dict version: str @@ -160,9 +152,6 @@ async def get_info(): parser_names = sorted( [re.sub(r'^(parsers?|missing)/', '', key) for key in parsers.parser_dict.keys()] ) - - config.load_plugins() - return InfoModel( **{ 'parsers': parser_names, @@ -186,14 +175,6 @@ async def get_info(): 'normalizers': [ normalizer.__name__ for normalizer in normalizing.normalizers ], - 'plugin_entry_points': [ - entry_point.dict_safe() - for entry_point in config.plugins.entry_points.filtered_values() - ], - 'plugin_packages': [ - plugin_package.dict() - for plugin_package in config.plugins.plugin_packages.values() - ], 'statistics': statistics(), 'search_quantities': { s.qualified_name: { diff --git a/nomad/cli/dev.py b/nomad/cli/dev.py index 3d6baf264c33ecbba15e9346edfd1110b160f34e..435008cb5202117871470bd188a4b6fe84a67c30 100644 --- a/nomad/cli/dev.py +++ b/nomad/cli/dev.py @@ -216,13 +216,6 @@ def get_gui_config() -> str: """ from nomad.config import config - config.load_plugins() - plugins = config.plugins.dict(exclude_unset=True) - for key in plugins['entry_points']['options'].keys(): - plugins['entry_points']['options'][key] = config.plugins.entry_points.options[ - key - ].dict_safe() - data = { 'appBase': config.ui.app_base, 'northBase': config.ui.north_base, @@ -240,7 +233,6 @@ def get_gui_config() -> str: 'servicesUploadLimit': config.services.upload_limit, 'appTokenMaxExpiresIn': config.services.app_token_max_expires_in, 'ui': config.ui.dict(exclude_none=True) if config.ui else {}, - 'plugins': plugins, } return f'window.nomadEnv = {json.dumps(data, indent=2)}' diff --git a/nomad/config/__init__.py b/nomad/config/__init__.py index c1851e85b2d19550a32be7c242f2af3c9aab2729..e274b5069784f406cbbea26f7a07e5041c60c266 100644 --- a/nomad/config/__init__.py +++ b/nomad/config/__init__.py @@ -117,8 +117,7 @@ def _merge(*args) -> Dict[str, Any]: root = args[0] for config in args[1:]: - if config: - root = merge_dicts(root, config) + root = merge_dicts(root, config) return root diff --git a/nomad/config/models/config.py b/nomad/config/models/config.py index 71e2239af4e6488f08faaa8a1410b20253183485..d82252db45ed87f4b20c7bf7c37c4d8f9aeaa41c 100644 --- a/nomad/config/models/config.py +++ b/nomad/config/models/config.py @@ -17,14 +17,13 @@ # import warnings -import sys import os import logging -from importlib.metadata import version, metadata +from importlib.metadata import version import yaml from typing import List, Union, Optional, Dict, Any -from pydantic import Field, root_validator, validator +from pydantic import Field, root_validator, validator, parse_obj_as import pkgutil try: @@ -33,17 +32,11 @@ except Exception: # noqa # package is not installed pass -if sys.version_info < (3, 10): - from importlib_metadata import entry_points -else: - from importlib.metadata import entry_points - - from .common import ( ConfigBaseModel, Options, ) -from .plugins import Plugins, EntryPointType, PluginPackage +from .plugins import Plugins from .north import NORTH from .ui import UI @@ -1008,24 +1001,6 @@ class Config(ConfigBaseModel): return values - def get_plugin_entry_point(self, id: str) -> EntryPointType: - """Returns the plugin entry point with the given id. It will - contain also any overrides included through nomad.yaml. - - Args: - id: The entry point identifier. Use the identifier given in the - plugin package pyproject.toml. - """ - try: - return self.plugins.entry_points.options[id] - except KeyError: - raise KeyError( - f'Could not find plugin entry point with id "{id}". Make sure that ' - 'the plugin package with an up-to-date pyproject.toml is installed and ' - 'that you are using the entry point id given in the plugin ' - 'pyproject.toml' - ) - def load_plugins(self): """Used to lazy-load the plugins. We cannot instantiate the plugins during the initialization of the nomad.config package, because it may @@ -1038,85 +1013,38 @@ class Config(ConfigBaseModel): cached_property decorator to the 'plugins' field instead of using this function. """ - from nomad.config import _plugins, _merge - from nomad.config.models.plugins import Parser, Normalizer, Schema - if self.plugins is None: - # Any plugins defined at the 'plugin' level are lifted to the - # 'plugin.entry_points'. - entry_points_config = _plugins.setdefault('entry_points', {}) - include_old = _plugins.get('include') - include_new = entry_points_config.get('include') - if include_old is not None: - if include_new is None: - entry_points_config['include'] = include_old - else: - entry_points_config['include'].extend(include_old) - entry_points_config.setdefault('exclude', []).extend( - _plugins.get('exclude', []) - ) - entry_points_config.setdefault('options', {}).update( - _plugins.get('options', {}) - ) + from nomad.config import _plugins + from nomad.config.models.plugins import Plugin - # Handle plugin entry_points (new plugin mechanism) - plugin_entry_point_ids = set() - plugin_entry_points = entry_points(group='nomad.plugin') - plugin_packages = {} - - for entry_point in plugin_entry_points: - key = entry_point.value - if key in plugin_entry_point_ids: - raise ValueError( - f'Could not load plugins due to duplicate entry_point name: "{key}".' - ) - package_name = entry_point.value.split('.', 1)[0] - config_override = ( - _plugins.get('entry_points', {}).get('options', {}).get(key, {}) - ) - config_override['id'] = key - config_instance = entry_point.load() - package_metadata = metadata(package_name) - url_list = package_metadata.get_all('Project-URL') - url_dict = {} - for url in url_list: - name, value = url.split(',') - url_dict[name.lower()] = value.strip() - if package_name not in plugin_packages: - plugin_package = PluginPackage( - name=package_name, - description=package_metadata.get('Summary'), - version=version(package_name), - homepage=url_dict.get('homepage'), - documentation=url_dict.get('documentation'), - repository=url_dict.get('repository'), - entry_points=[key], - ) - plugin_packages[package_name] = plugin_package - else: - plugin_packages[package_name].entry_points.append(key) - config_default = config_instance.dict(exclude_unset=True) - config_default['plugin_package'] = package_name - config_class = config_instance.__class__ - config_final = config_class.parse_obj( - _merge(config_default, config_override) - ) - _plugins['entry_points']['options'][key] = config_final - plugin_entry_point_ids.add(key) - _plugins['plugin_packages'] = plugin_packages - - # Handle plugins defined in nomad.yaml (old plugin mechanism) - def load_plugin_yaml(name, values: Dict[str, Any]): + def load_plugin(values: Dict[str, Any]): """Loads plugin metadata from nomad_plugin.yaml""" python_package = values.get('python_package') if not python_package: - raise ValueError( - f'Could not find python_package for plugin entry point: {name}.' - ) + raise ValueError('Python plugins must provide a python_package.') package_path = values.get('package_path') if package_path is None: - package_path = get_package_path(python_package) + try: + # We try to deduce the package path from the top-level package + package_path_segments = python_package.split('.') + root_package = package_path_segments[0] + package_dirs = package_path_segments[1:] + package_path = os.path.join( + os.path.dirname( + pkgutil.get_loader(root_package).get_filename() # type: ignore + ), + *package_dirs, + ) + if not os.path.isdir(package_path): + # We could not find it this way. Let's try to official way + package_path = os.path.dirname( + pkgutil.get_loader(python_package).get_filename() # type: ignore + ) + except Exception as e: + raise ValueError( + f'The python package {python_package} cannot be loaded.', e + ) values['package_path'] = package_path metadata_path = os.path.join(package_path, 'nomad_plugin.yaml') @@ -1136,41 +1064,5 @@ class Config(ConfigBaseModel): return values for key, plugin in _plugins['options'].items(): - if key not in plugin_entry_point_ids: - plugin_config = load_plugin_yaml(key, plugin) - plugin_config['id'] = key - plugin_class = { - 'parser': Parser, - 'normalizer': Normalizer, - 'schema': Schema, - }.get(plugin_config['plugin_type']) - _plugins['entry_points']['options'][key] = plugin_class.parse_obj( - plugin_config - ) - + _plugins['options'][key] = parse_obj_as(Plugin, load_plugin(plugin)) self.plugins = Plugins.parse_obj(_plugins) - - -def get_package_path(package_name: str) -> str: - """Given a python package name, returns the filepath of the package root folder.""" - package_path = None - try: - # We try to deduce the package path from the top-level package - package_path_segments = package_name.split('.') - root_package = package_path_segments[0] - package_dirs = package_path_segments[1:] - package_path = os.path.join( - os.path.dirname( - pkgutil.get_loader(root_package).get_filename() # type: ignore - ), - *package_dirs, - ) - if not os.path.isdir(package_path): - # We could not find it this way. Let's try to official way - package_path = os.path.dirname( - pkgutil.get_loader(python_package).get_filename() # type: ignore - ) - except Exception as e: - raise ValueError(f'The python package {package_name} cannot be loaded.', e) - - return package_path diff --git a/nomad/config/models/plugins.py b/nomad/config/models/plugins.py index d81126bde307d25ddb7927f14786760fb9f46b87..e4797fe8b5247972cf603cebafdd7d82fca9a0a3 100644 --- a/nomad/config/models/plugins.py +++ b/nomad/config/models/plugins.py @@ -17,154 +17,12 @@ # import sys -from abc import ABCMeta, abstractmethod import importlib -from typing import Optional, Dict, Union, List, Literal, TYPE_CHECKING +from typing_extensions import Annotated +from typing import Optional, Dict, Union, List, Literal from pydantic import BaseModel, Field from .common import Options -from .ui import App - -if TYPE_CHECKING: - from nomad.metainfo import SchemaPackage - from nomad.normalizing import Normalizer as NormalizerBaseClass - from nomad.parsing import Parser as ParserBaseClass - - -class EntryPoint(BaseModel): - """Base model for a NOMAD plugin entry points.""" - - id: Optional[str] = Field( - description='Unique identifier corresponding to the entry point name. Automatically set to the plugin entry point name in pyproject.toml.' - ) - entry_point_type: str = Field(description='Determines the entry point type.') - name: Optional[str] = Field(description='Name of the plugin entry point.') - description: Optional[str] = Field( - description='A human readable description of the plugin entry point.' - ) - plugin_package: Optional[str] = Field( - description='The plugin package from which this entry points comes from.' - ) - - def dict_safe(self): - """Used to serialize the non-confidential parts of a plugin model. This - function can be overridden in subclasses to expose more information. - """ - return self.dict(include=EntryPoint.__fields__.keys(), exclude_none=True) - - -class AppEntryPoint(EntryPoint): - entry_point_type: Literal['app'] = Field( - 'app', description='Determines the entry point type.' - ) - app: App = Field(description='The app configuration.') - - def dict_safe(self): - return self.dict(include=AppEntryPoint.__fields__.keys(), exclude_none=True) - - -class SchemaPackageEntryPoint(EntryPoint, metaclass=ABCMeta): - entry_point_type: Literal['schema_package'] = Field( - 'schema_package', description='Specifies the entry point type.' - ) - - @abstractmethod - def load(self) -> 'SchemaPackage': - """Used to lazy-load a schema package instance. You should override this - method in your subclass. Note that any Python module imports required - for the schema package should be done within this function as well.""" - pass - - -class NormalizerEntryPoint(EntryPoint, metaclass=ABCMeta): - entry_point_type: Literal['normalizer'] = Field( - 'normalizer', description='Determines the entry point type.' - ) - - @abstractmethod - def load(self) -> 'NormalizerBaseClass': - """Used to lazy-load a normalizer instance. You should override this - method in your subclass. Note that any Python module imports required - for the normalizer class should be done within this function as well.""" - pass - - -class ParserEntryPoint(EntryPoint, metaclass=ABCMeta): - entry_point_type: Literal['parser'] = Field( - 'parser', description='Determines the entry point type.' - ) - level: int = Field( - 0, - description=""" - The order by which the parser is executed with respect to other parsers. - """, - ) - - mainfile_contents_re: Optional[str] = Field( - description=""" - A regular expression that is applied the content of a potential mainfile. - If this expression is given, the parser is only considered for a file, if the - expression matches. - """ - ) - mainfile_name_re: str = Field( - r'.*', - description=""" - A regular expression that is applied the name of a potential mainfile. - If this expression is given, the parser is only considered for a file, if the - expression matches. - """, - ) - mainfile_mime_re: str = Field( - r'.*', - description=""" - A regular expression that is applied the mime type of a potential - mainfile. If this expression is given, the parser is only considered - for a file, if the expression matches. - """, - ) - mainfile_binary_header: Optional[bytes] = Field( - description=""" - Matches a binary file if the given bytes are included in the file. - """, - exclude=True, - ) - mainfile_binary_header_re: Optional[bytes] = Field( - description=""" - Matches a binary file if the given binary regular expression bytes matches the - file contents. - """, - exclude=True, - ) - mainfile_alternative: bool = Field( - False, - description=""" - If True, the parser only matches a file, if no other file in the same directory - matches a parser. - """, - ) - mainfile_contents_dict: Optional[dict] = Field( - description=""" - Is used to match structured data files like JSON or HDF5. - """ - ) - supported_compressions: List[str] = Field( - [], - description=""" - Files compressed with the given formats (e.g. xz, gz) are uncompressed and - matched like normal files. - """, - ) - - @abstractmethod - def load(self) -> 'ParserBaseClass': - """Used to lazy-load a parser instance. You should override this method - in your subclass. Note that any Python module imports required for the - parser class should be done within this function as well.""" - pass - - def dict_safe(self): - return self.dict(include=ParserEntryPoint.__fields__.keys(), exclude_none=True) class PluginBase(BaseModel): @@ -175,10 +33,6 @@ class PluginBase(BaseModel): Parser or Schema. """ - plugin_type: str = Field( - description='The type of the plugin.', - ) - id: Optional[str] = Field(description='The unique identifier for this plugin.') name: str = Field( description='A short descriptive human readable name for the plugin.' ) @@ -192,12 +46,6 @@ class PluginBase(BaseModel): description='The URL of the plugins main source code repository.' ) - def dict_safe(self): - """Used to serialize the non-confidential parts of a plugin model. This - function can be overridden in subclasses to expose more information. - """ - return self.dict(include=PluginBase.__fields__.keys(), exclude_none=True) - class PythonPluginBase(PluginBase): """ @@ -325,15 +173,13 @@ class Parser(PythonPluginBase): mainfile_binary_header: Optional[bytes] = Field( description=""" Matches a binary file if the given bytes are included in the file. - """, - exclude=True, + """ ) mainfile_binary_header_re: Optional[bytes] = Field( description=""" Matches a binary file if the given binary regular expression bytes matches the file contents. - """, - exclude=True, + """ ) mainfile_alternative: bool = Field( False, @@ -385,7 +231,6 @@ class Parser(PythonPluginBase): from nomad.parsing.parser import MatchingParserInterface data = self.dict() - del data['id'] del data['description'] del data['python_package'] del data['plugin_type'] @@ -396,57 +241,13 @@ class Parser(PythonPluginBase): return MatchingParserInterface(**data) -EntryPointType = Union[ - Schema, - Normalizer, - Parser, - SchemaPackageEntryPoint, - ParserEntryPoint, - NormalizerEntryPoint, - AppEntryPoint, +Plugin = Annotated[ + Union[Schema, Normalizer, Parser], Field(discriminator='plugin_type') ] -class EntryPoints(Options): - options: Dict[str, EntryPointType] = Field( - dict(), description='The available plugin entry points.' - ) - - -class PluginPackage(BaseModel): - name: str = Field( - description='Name of the plugin Python package, read from pyproject.toml.' - ) - description: Optional[str] = Field( - description='Package description, read from pyproject.toml.' - ) - version: Optional[str] = Field( - description='Plugin package version, read from pyproject.toml.' - ) - homepage: Optional[str] = Field( - description='Link to the plugin package homepage, read from pyproject.toml.' - ) - documentation: Optional[str] = Field( - description='Link to the plugin package documentation page, read from pyproject.toml.' - ) - repository: Optional[str] = Field( - description='Link to the plugin package source code repository, read from pyproject.toml.' - ) - entry_points: List[str] = Field( - description='List of entry point ids contained in this package, read form pyproject.toml' - ) - - -class Plugins(BaseModel): - entry_points: EntryPoints = Field( - description='Used to control plugin entry points.' - ) - plugin_packages: Dict[str, PluginPackage] = Field( - description=""" - Contains the installed installed plugin packages with the package name - used as a key. This is autogenerated and should not be modified. - """ - ) +class Plugins(Options): + options: Dict[str, Plugin] = Field(dict(), description='The available plugin.') def add_plugin(plugin: Schema) -> None: @@ -458,7 +259,7 @@ def add_plugin(plugin: Schema) -> None: sys.path.insert(0, plugin.package_path) # Add plugin to config - config.plugins.entry_points.options[plugin.key] = plugin + config.plugins.options[plugin.key] = plugin # Add plugin to Package registry package = importlib.import_module(plugin.python_package) @@ -482,7 +283,7 @@ def remove_plugin(plugin) -> None: pass # Remove package as plugin - del config.plugins.entry_points.options[plugin.key] + del config.plugins.options[plugin.key] # Remove plugin from Package registry package = importlib.import_module(plugin.python_package).m_package diff --git a/nomad/datamodel/__init__.py b/nomad/datamodel/__init__.py index 370ab0775e1845ed24c84a8fa833a0b3a9c62cc5..582a8ed56a5299ad4e834f0c7d058e83a6df79a5 100644 --- a/nomad/datamodel/__init__.py +++ b/nomad/datamodel/__init__.py @@ -34,7 +34,6 @@ from .optimade import OptimadeEntry, Species from .metainfo import m_env from .results import Results from .data import EntryData, ArchiveSection -from nomad.config.models.plugins import SchemaPackageEntryPoint from .context import Context, ClientContext, ServerContext m_env.m_add_sub_section( @@ -63,11 +62,9 @@ def all_metainfo_packages(): from nomad.config import config config.load_plugins() - for entry_point in config.plugins.entry_points.filtered_values(): - if isinstance(entry_point, PythonPluginBase): - entry_point.import_python_package() - if isinstance(entry_point, SchemaPackageEntryPoint): - entry_point.load() + for plugin in config.plugins.filtered_values(): + if isinstance(plugin, PythonPluginBase): + plugin.import_python_package() # Importing the parsers will also make sure that related schemas will be imported # even if they are not part of the plugin's python package as this will import diff --git a/nomad/datamodel/data.py b/nomad/datamodel/data.py index 5c4d738df7a446dadcc534f19fccf1fb3b7c7a3c..73595a44ed730023f0e4d86e009777ee32267c9a 100644 --- a/nomad/datamodel/data.py +++ b/nomad/datamodel/data.py @@ -257,4 +257,3 @@ class AuthorReference(Reference): author_reference = AuthorReference() predefined_datatypes['Author'] = author_reference -Schema = EntryData diff --git a/nomad/datamodel/metainfo/__init__.py b/nomad/datamodel/metainfo/__init__.py index 5df06327f442fab002bdd6318ae66c666f4bd228..d13c4beaf48778cd391dd3ae24b6624a538d009d 100644 --- a/nomad/datamodel/metainfo/__init__.py +++ b/nomad/datamodel/metainfo/__init__.py @@ -48,13 +48,13 @@ class SchemaInterface: simulationworkflowschema, runschema = None, None config.load_plugins() -for entry_point in config.plugins.entry_points.filtered_values(): - if isinstance(entry_point, Schema): - if entry_point.name == 'simulationworkflowschema': - simulationworkflowschema = SchemaInterface(entry_point.python_package) - elif entry_point.name == 'runschema': - runschema = SchemaInterface(entry_point.python_package) +for plugin in config.plugins.filtered_values(): + if isinstance(plugin, Schema): + if plugin.name == 'simulationworkflowschema': + simulationworkflowschema = SchemaInterface(plugin.python_package) + elif plugin.name == 'runschema': + runschema = SchemaInterface(plugin.python_package) else: - importlib.import_module(entry_point.python_package) + importlib.import_module(plugin.python_package) SCHEMA_IMPORT_ERROR = 'Schema not defined.' diff --git a/nomad/datamodel/metainfo/eln/__init__.py b/nomad/datamodel/metainfo/eln/__init__.py index 29741b2c4bbd74884639eec8093a724d66131aad..1799f385464984ca4938a23535ba07d381121a54 100644 --- a/nomad/datamodel/metainfo/eln/__init__.py +++ b/nomad/datamodel/metainfo/eln/__init__.py @@ -1038,10 +1038,10 @@ class ElnWithStructureFile(ArchiveSection): if system_normalizer_cls: system_normalizer = system_normalizer_cls(archive) system_normalizer.normalize() - optimade_normalizer = OptimadeNormalizer() - optimade_normalizer.normalize(archive) - results_normalizer = ResultsNormalizer() - results_normalizer.normalize(archive) + optimade_normalizer = OptimadeNormalizer(archive) + optimade_normalizer.normalize() + results_normalizer = ResultsNormalizer(archive) + results_normalizer.normalize() # TODO: rewrite it in a way in which the run section is not needed and System is # directly added to the archive.data diff --git a/nomad/files.py b/nomad/files.py index de4776bf1fed385536f8b5f0d56efae874662da5..7f1e8a2e8169784bf7709d8809bcf6b7d82c863e 100644 --- a/nomad/files.py +++ b/nomad/files.py @@ -1520,12 +1520,6 @@ class PublicUploadFiles(UploadFiles): self._archive_msg_file_object = archive_msg_file_object self._archive_hdf5_file_object = archive_hdf5_file_object self._access = access - - files_found = True - self._raw_zip_file_object = raw_zip_file_object - self._archive_msg_file_object = archive_msg_file_object - self._archive_hdf5_file_object = archive_hdf5_file_object - self._access = access if not files_found: raise KeyError('Neither public nor restricted files found') return self._access diff --git a/nomad/metainfo/__init__.py b/nomad/metainfo/__init__.py index 382c6e43cc1edcef3b624fcf6fad1a6cefe03645..be07523c0c4e3207e3085d9f4210291978d68199 100644 --- a/nomad/metainfo/__init__.py +++ b/nomad/metainfo/__init__.py @@ -42,7 +42,6 @@ from .metainfo import ( Section, Category, Package, - SchemaPackage, Environment, MEnum, Datetime, diff --git a/nomad/metainfo/elasticsearch_extension.py b/nomad/metainfo/elasticsearch_extension.py index 422adb9d9cb08c26af30cdf50701d16ffe2874e9..a444a8db962cf890e0435d659474d54fe3010de0 100644 --- a/nomad/metainfo/elasticsearch_extension.py +++ b/nomad/metainfo/elasticsearch_extension.py @@ -177,7 +177,6 @@ from elasticsearch_dsl import Q from nomad import utils from nomad.config import config -from nomad.config.models.plugins import Schema, Parser, SchemaPackageEntryPoint from nomad.metainfo.util import MTypes from .metainfo import ( @@ -502,27 +501,14 @@ class DocumentType: # Gather the list of enabled schema plugins. Raise error if duplicate # package name is encountered. - package_names = set() - packages_from_plugins = {} - for plugin in config.plugins.entry_points.filtered_values(): - if isinstance(plugin, (Schema, Parser)): - package_name = plugin.python_package - if package_name in package_names: + packages = set() + for plugin in config.plugins.filtered_values(): + if plugin.plugin_type in ['schema', 'parser']: + if plugin.python_package in packages: raise ValueError( f'Your plugin configuration contains two packages with the same name: {plugin.python_package}.' ) - package_names.add(package_name) - elif isinstance(plugin, SchemaPackageEntryPoint): - packages_from_plugins[plugin.id] = plugin.load() - for name, package in Package.registry.items(): - if not name: - logger.warning( - f'no name defined for package {package}, could not load dynamic quantities' - ) - continue - package_name = name.split('.')[0] - if package_name in package_names: - packages_from_plugins[name] = package + packages.add(plugin.python_package) # Use to get all quantities from the given definition, TODO: Circular # definitions are here avoided by simply keeping track of which @@ -550,21 +536,30 @@ class DocumentType: yield item quantities_dynamic = {} - for package in packages_from_plugins.values(): - for section in package.section_definitions: - if isinstance(section, Section) and issubclass( - section.section_cls, EntryData - ): - schema_name = section.qualified_name() - for quantity_def, path, repeats in get_all_quantities(section): - annotation = create_dynamic_quantity_annotation(quantity_def) - if not annotation: - continue - full_name = f'data.{path}{schema_separator}{schema_name}' - search_quantity = SearchQuantity( - annotation, qualified_name=full_name, repeats=repeats - ) - quantities_dynamic[full_name] = search_quantity + for name, package in Package.registry.items(): + if not name: + logger.warning( + f'no name defined for package {package}, could not load dynamic quantities' + ) + continue + package_name = name.split('.')[0] + if package_name in packages: + for section in package.section_definitions: + if isinstance(section, Section) and issubclass( + section.section_cls, EntryData + ): + schema_name = section.qualified_name() + for quantity_def, path, repeats in get_all_quantities(section): + annotation = create_dynamic_quantity_annotation( + quantity_def + ) + if not annotation: + continue + full_name = f'data.{path}{schema_separator}{schema_name}' + search_quantity = SearchQuantity( + annotation, qualified_name=full_name, repeats=repeats + ) + quantities_dynamic[full_name] = search_quantity self.quantities.update(quantities_dynamic) def _register(self, annotation, prefix, repeats): diff --git a/nomad/metainfo/metainfo.py b/nomad/metainfo/metainfo.py index ae8bcd2bd1ed92652ff045fa115548de0997ea01..3c666b307f9c0405ec5059b76a5b52fb92272f4f 100644 --- a/nomad/metainfo/metainfo.py +++ b/nomad/metainfo/metainfo.py @@ -5264,4 +5264,3 @@ class AnnotationModel(Annotation, BaseModel): AnnotationModel.update_forward_refs() -SchemaPackage = Package diff --git a/nomad/mkdocs.py b/nomad/mkdocs.py index 52941dfdc7387f53fccb510a2edbe0f1b4540b9e..ee06a625d6f719464c8422ccb3b0fed1cc641503 100644 --- a/nomad/mkdocs.py +++ b/nomad/mkdocs.py @@ -33,7 +33,7 @@ from markdown.extensions.toc import slugify from nomad.utils import strip from nomad.config import config -from nomad.config.models.plugins import Parser, EntryPointType +from nomad.config.models.plugins import Parser, Plugin from nomad.app.v1.models import query_documentation, owner_documentation from nomad.app.v1.routers.entries import archive_required_documentation from nomad import utils @@ -383,7 +383,7 @@ def define_env(env): def parser_list(): # pylint: disable=unused-variable parsers = [ plugin - for _, plugin in config.plugins.entry_points.filtered_items() + for _, plugin in config.plugins.filtered_items() if isinstance(plugin, Parser) ] @@ -446,21 +446,13 @@ def define_env(env): @env.macro def plugin_list(): # pylint: disable=unused-variable - plugins = [plugin for plugin in config.plugins.entry_points.options.values()] + plugins = [plugin for plugin in config.plugins.options.values()] - def render_plugin(plugin: EntryPointType) -> str: + def render_plugin(plugin: Plugin) -> str: result = plugin.name - docs_or_code_url = None - for field in [ - 'plugin_documentation_url', - 'plugin_source_code_url', - 'documentation', - 'repository', - ]: - value = getattr(plugin, field, None) - if value: - dosc_or_code_url = value - break + docs_or_code_url = ( + plugin.plugin_documentation_url or plugin.plugin_source_code_url + ) if docs_or_code_url: result = f'[{plugin.name}]({docs_or_code_url})' if plugin.description: @@ -470,9 +462,7 @@ def define_env(env): categories = {} for plugin in plugins: - category = getattr( - plugin, 'plugin_type', getattr(plugin, 'entry_point_type', None) - ) + category = plugin.python_package.split('.')[0] categories.setdefault(category, []).append(plugin) return '\n\n'.join( diff --git a/nomad/normalizing/__init__.py b/nomad/normalizing/__init__.py index 43da668aa0bd223abfcb9d78531ae78a1c4b5e3d..ec5c58b532584b045f0c59d87ce9c5a1b395ddcb 100644 --- a/nomad/normalizing/__init__.py +++ b/nomad/normalizing/__init__.py @@ -42,10 +42,7 @@ from collections import UserList from nomad.config import config -from nomad.config.models.plugins import ( - Normalizer as OldNormalizerPlugin, - NormalizerEntryPoint, -) +from nomad.config.models.plugins import Normalizer as NormalizerPlugin from .normalizer import Normalizer @@ -67,7 +64,6 @@ class NormalizerInterface: def __init__(self, path: str) -> None: self._path = path self._cls = None - self.archive = None @property def normalizer_class(self): @@ -76,49 +72,21 @@ class NormalizerInterface: return self._cls def normalize(self, logger=None): - self.normalizer.normalize(self.archive, logger) + self.normalizer_class.normalize(logger) def __call__(self, *args: Any) -> Any: - self.archive = args[0] - self.normalizer = self.normalizer_class(*args[1:]) - return self + return self.normalizer_class(*args) def __getattr__(self, name: str): return getattr(self.normalizer_class, name, None) -class NormalizerInterfaceNew: - def __init__(self, normalizer: Normalizer) -> None: - self.normalizer = normalizer - self.archive = None - - def normalize(self, logger=None): - self.normalizer.normalize(self.archive, logger) - - def __call__(self, *args: Any) -> Any: - self.archive = args[0] - return self - - def __getattr__(self, name: str): - if name == '__name__': - return self.normalizer.__class__.__name__ - return getattr(self.normalizer, name, None) - - -config.load_plugins() -enabled_entry_points = config.plugins.entry_points.filtered_values() normalizers = SortedNormalizers([]) -# Load normalizers using old plugin mechanism -for entry_point in enabled_entry_points: - if isinstance(entry_point, OldNormalizerPlugin): - normalizers.append(NormalizerInterface(entry_point.normalizer_class_name)) -# Load normalizers using old config mechanism +for plugin_name, plugin in config.plugins.options.items(): + if isinstance(plugin, NormalizerPlugin) and config.plugins.filter(plugin_name): + normalizers.append(NormalizerInterface(plugin.normalizer_class_name)) + for normalizer in config.normalize.normalizers.filtered_values(): normalizers.append(NormalizerInterface(normalizer)) - -# Load normalizers using new plugin mechanism -for entry_point in enabled_entry_points: - if isinstance(entry_point, NormalizerEntryPoint): - normalizers.append(NormalizerInterfaceNew(entry_point.load())) diff --git a/nomad/normalizing/metainfo.py b/nomad/normalizing/metainfo.py index 81fdf44b1fbbd54b09b4c86f35d914cae37b0758..7eefb5f9c84414be918875e54b672dd471e17256 100644 --- a/nomad/normalizing/metainfo.py +++ b/nomad/normalizing/metainfo.py @@ -16,17 +16,15 @@ # limitations under the License. # -from nomad.datamodel import EntryArchive from nomad.datamodel.data import ArchiveSection -from nomad.datamodel import EntryArchive -from typing import Optional +from typing import List, Optional from . import Normalizer class MetainfoNormalizer(Normalizer): domain: Optional[str] = None - def normalize_section(self, archive: EntryArchive, section, logger): + def normalize_section(self, section, logger): normalize = None try: normalize = getattr(section, 'normalize') @@ -35,7 +33,7 @@ class MetainfoNormalizer(Normalizer): if normalize: try: - normalize(archive, logger) + normalize(self.entry_archive, logger) except Exception as e: logger.error( 'could not normalize section', @@ -43,7 +41,7 @@ class MetainfoNormalizer(Normalizer): exc_info=e, ) - def normalize(self, archive: EntryArchive, logger=None) -> None: + def normalize(self, logger=None) -> None: if logger is None: from nomad import utils @@ -60,6 +58,6 @@ class MetainfoNormalizer(Normalizer): ) for sub_section in sub_sections: _normalize(sub_section) - self.normalize_section(archive, section, logger) + self.normalize_section(section, logger) - _normalize(archive) + _normalize(self.entry_archive) diff --git a/nomad/normalizing/normalizer.py b/nomad/normalizing/normalizer.py index 335249b45895fdd0bee82fccae7972a13820972f..145ec4db8e4ac00a7fecc0b64531bbd6f7d0caf5 100644 --- a/nomad/normalizing/normalizer.py +++ b/nomad/normalizing/normalizer.py @@ -26,10 +26,11 @@ from nomad.datamodel import EntryArchive class Normalizer(metaclass=ABCMeta): """ - A base class for normalizers. Only one instance of the Normalizer is - created, and you should perform any heavy initialization in the constructor. - The normalize method is called on archives, and this function should ideally - not mutate the state of the shared normalizer instance. + A base class for normalizers. Normalizers work on a :class:`EntryArchive` section + for read and write. Normalizer instances are reused. + + Arguments: + entry_archive: The entry_archive root section of the archive to normalize. """ domain: Optional[str] = 'dft' @@ -38,11 +39,16 @@ class Normalizer(metaclass=ABCMeta): """ Specifies the order of normalization with respect to other normalizers. Lower level is executed first.""" - def __init__(self, **kwargs) -> None: + def __init__(self, entry_archive: EntryArchive) -> None: + self.entry_archive = entry_archive + try: + self.section_run = entry_archive.run[0] + except (AttributeError, IndexError): + self.section_run = None self.logger = get_logger(__name__) @abstractmethod - def normalize(self, archive: EntryArchive, logger=None) -> None: + def normalize(self, logger=None) -> None: if logger is not None: self.logger = logger.bind(normalizer=self.__class__.__name__) @@ -59,8 +65,8 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): only_representatives: Will only normalize the `representative` systems. """ - def __init__(self, only_representatives: bool = False, **kwargs): - super().__init__(**kwargs) + def __init__(self, entry_archive: EntryArchive, only_representatives: bool = False): + super().__init__(entry_archive) self.only_representatives = only_representatives @property @@ -74,17 +80,15 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): 'configuration_periodic_dimensions', ] - def _normalize_system(self, archive, system, is_representative): - return self.normalize_system(archive, system, is_representative) + def _normalize_system(self, system, is_representative): + return self.normalize_system(system, is_representative) @abstractmethod - def normalize_system( - self, archive: EntryArchive, system: MSection, is_representative: bool - ) -> bool: - """Normalize the given section and returns True, if successful""" + def normalize_system(self, system: MSection, is_representative: bool) -> bool: + """Normalize the given section and returns True, iff successful""" pass - def __representative_system(self, archive): + def __representative_system(self): """Used to select a representative system for this entry. Attempt to find a single section_system that is representative for the @@ -95,7 +99,7 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): # Try to find workflow information and select the representative system # based on it - workflow = archive.workflow2 + workflow = self.entry_archive.workflow2 if workflow: try: @@ -110,7 +114,7 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): # available in reverse order until a valid one is found. if system is None: try: - sccs = archive.run[0].calculation + sccs = self.section_run.calculation for iscc in reversed(sccs): isys = iscc.system_ref if isys is not None: @@ -123,7 +127,7 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): # If no sccs exist, try to find systems if system is None: try: - system = archive.run[0].system[-1] + system = self.section_run.system[-1] except Exception: system = None @@ -142,17 +146,17 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): pass if scc is not None: - archive.run[0].m_cache['representative_scc_idx'] = scc.m_parent_index + self.section_run.m_cache['representative_scc_idx'] = scc.m_parent_index if system is not None: - archive.run[0].m_cache['representative_system_idx'] = system.m_parent_index + self.section_run.m_cache['representative_system_idx'] = ( + system.m_parent_index + ) return system.m_resolved() if system is not None else None - def __normalize_system( - self, archive: EntryArchive, system, representative, logger=None - ) -> bool: + def __normalize_system(self, system, representative, logger=None) -> bool: try: - return self._normalize_system(archive, system, representative) + return self._normalize_system(system, representative) except KeyError as e: self.logger.error( @@ -176,22 +180,22 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta): ) raise e - def normalize(self, archive: EntryArchive, logger=None) -> None: - super().normalize(archive, logger) + def normalize(self, logger=None) -> None: + super().normalize(logger) # If no section run detected, do nothing - if not archive.run: + if self.section_run is None: return # Process representative system first repr_sys_idx = None - repr_sys = self.__representative_system(archive) + repr_sys = self.__representative_system() if repr_sys is not None: repr_sys_idx = repr_sys.m_parent_index self.logger.info('chose "representative" section system') - self.__normalize_system(archive, repr_sys, True, logger) + self.__normalize_system(repr_sys, True, logger) # All the rest if requested if not self.only_representatives: - for isys, system in enumerate(archive.run[0].system): + for isys, system in enumerate(self.section_run.system): if isys != repr_sys_idx: - self.__normalize_system(archive, system, False, logger) + self.__normalize_system(system, False, logger) diff --git a/nomad/normalizing/optimade.py b/nomad/normalizing/optimade.py index c8f4ea6bc19c826e8a2dd4abfc675fcdeb740c9c..77db87723b3570041bc15c53121a6c15f0f9d218 100644 --- a/nomad/normalizing/optimade.py +++ b/nomad/normalizing/optimade.py @@ -23,7 +23,6 @@ import ase.data import ase.formula import pint.quantity -from nomad.datamodel import EntryArchive from nomad.atomutils import Formula from nomad.normalizing.normalizer import SystemBasedNormalizer from nomad.units import ureg @@ -89,18 +88,18 @@ class OptimadeNormalizer(SystemBasedNormalizer): It assumes that the :class:`SystemNormalizer` was run before. """ - def __init__(self): - super().__init__(only_representatives=True) + def __init__(self, archive): + super().__init__(archive, only_representatives=True) - def add_optimade_data(self, archive: EntryArchive) -> OptimadeEntry: + def add_optimade_data(self, index) -> OptimadeEntry: """ The 'main' method of this :class:`SystemBasedNormalizer`. Normalizes the section with the given `index`. Normalizes geometry, classifies, system_type, and runs symmetry analysis. """ - if archive.metadata is None: - archive.m_create(EntryMetadata) - optimade = archive.metadata.m_create(OptimadeEntry) + if self.entry_archive.metadata is None: + self.entry_archive.m_create(EntryMetadata) + optimade = self.entry_archive.metadata.m_create(OptimadeEntry) def get_value( quantity_def, @@ -110,7 +109,7 @@ class OptimadeNormalizer(SystemBasedNormalizer): source: Any = None, ) -> Any: try: - source = source if source is not None else archive.run[0].system[-1] + source = source if source is not None else self.section_run.system[-1] value = source.m_get(quantity_def) if value is None: return @@ -129,7 +128,7 @@ class OptimadeNormalizer(SystemBasedNormalizer): except KeyError: return default - system = archive.run[0].system[-1] if archive.run[0].system else None + system = self.section_run.system[-1] if self.section_run.system else None if system is None: return optimade @@ -154,9 +153,9 @@ class OptimadeNormalizer(SystemBasedNormalizer): ] # formulas - system_cls = ( - archive.run[0].m_def.all_sub_sections['system'].sub_section.section_cls - ) + system_cls = self.section_run.m_def.all_sub_sections[ + 'system' + ].sub_section.section_cls original_formula = get_value( system_cls.chemical_composition_hill, source=system ) @@ -199,12 +198,12 @@ class OptimadeNormalizer(SystemBasedNormalizer): return optimade - def normalize_system(self, archive: EntryArchive, system, is_representative): + def normalize_system(self, system, is_representative): if not is_representative: return False try: - self.add_optimade_data(archive) + self.add_optimade_data(system.m_parent_index) return True except Exception as e: diff --git a/nomad/normalizing/porosity.py b/nomad/normalizing/porosity.py index d780765f54f4e4951c7ac4227face50fb0caa69b..17ea50d7dcb6fc528f404d8f7b990d222a0dbd1e 100644 --- a/nomad/normalizing/porosity.py +++ b/nomad/normalizing/porosity.py @@ -63,8 +63,8 @@ class PorosityNormalizer(SystemBasedNormalizer): In the future, the it will also compute the rcsr tological code. """ - def __init__(self): - super().__init__(only_representatives=True) + def __init__(self, archive): + super().__init__(archive, only_representatives=True) def normalize_system(self, system, is_representative): if not is_representative: diff --git a/nomad/normalizing/results.py b/nomad/normalizing/results.py index edb0ac873cb93a9c3621e938928093009721f7fc..936e91583415e24f4ea425956e7581384e460598 100644 --- a/nomad/normalizing/results.py +++ b/nomad/normalizing/results.py @@ -30,7 +30,6 @@ from nomad.atomutils import Formula from nomad.normalizing.normalizer import Normalizer from nomad.normalizing.method import MethodNormalizer from nomad.normalizing.material import MaterialNormalizer -from nomad.datamodel import EntryArchive from nomad.datamodel.metainfo.workflow import Workflow from nomad.datamodel.data import ArchiveSection from nomad.normalizing.common import structures_2d @@ -103,10 +102,7 @@ class ResultsNormalizer(Normalizer): domain = None normalizer_level = 3 - def normalize(self, archive: EntryArchive, logger=None) -> None: - self.entry_archive = archive - self.section_run = archive.run[0] if archive.run else None - + def normalize(self, logger=None) -> None: # Setup logger if logger is not None: self.logger = logger.bind(normalizer=self.__class__.__name__) @@ -123,9 +119,6 @@ class ResultsNormalizer(Normalizer): for measurement in self.entry_archive.measurement: self.normalize_measurement(measurement) - self.entry_archive = None - self.section_run = None - def normalize_sample(self, sample) -> None: material = self.entry_archive.m_setdefault('results.material') diff --git a/nomad/normalizing/topology.py b/nomad/normalizing/topology.py index a2a7bf774c1d1cb7e7d699ab2f1c159bfce47dfd..00415768d94c996eff10e013405d2b234baa73fb 100644 --- a/nomad/normalizing/topology.py +++ b/nomad/normalizing/topology.py @@ -40,6 +40,7 @@ from matid.classification.classifications import ( from nomad import utils from nomad.config import config from nomad import atomutils +from nomad.units import ureg from nomad.datamodel.results import ( CoreHole, SymmetryNew as Symmetry, diff --git a/nomad/parsing/parser.py b/nomad/parsing/parser.py index 38b671ed2fe40ceab424898aa8e017a8683de06c..513ae79202c8c8bbfea713204c30404d94743d7f 100644 --- a/nomad/parsing/parser.py +++ b/nomad/parsing/parser.py @@ -216,7 +216,6 @@ class MatchingParser(Parser): domain='dft', metadata: dict = None, supported_compressions: List[str] = [], - **kwargs, ) -> None: super().__init__() diff --git a/nomad/parsing/parsers.py b/nomad/parsing/parsers.py index 8b5aa9836fa28854d1ffe9e4bbbd78fdff3d5865..c35340e39884964775b480382e4c434e482068ee 100644 --- a/nomad/parsing/parsers.py +++ b/nomad/parsing/parsers.py @@ -21,7 +21,7 @@ from typing import Optional, Tuple, List, Dict from collections.abc import Iterable from nomad.config import config -from nomad.config.models.plugins import Parser as ParserPlugin, ParserEntryPoint +from nomad.config.models.plugins import Parser as ParserPlugin from nomad.datamodel import EntryArchive, EntryMetadata, results from nomad.datamodel.context import Context, ClientContext @@ -226,27 +226,13 @@ def run_parser( parsers = [GenerateRandomParser(), TemplateParser(), ChaosParser()] -config.load_plugins() -enabled_entry_points = config.plugins.entry_points.filtered_values() - -# Load parsers using old plugin mechanism parsers.extend( [ - entry_point.create_matching_parser_interface() - for entry_point in enabled_entry_points - if isinstance(entry_point, ParserPlugin) + plugin.create_matching_parser_interface() + for plugin_name, plugin in config.plugins.options.items() + if config.plugins.filter(plugin_name) and isinstance(plugin, ParserPlugin) ] ) - -# Load parsers using new plugin mechanism. The entry point name is used to -# identify the parser. -for entry_point in enabled_entry_points: - if isinstance(entry_point, ParserEntryPoint): - entry_point_name = entry_point.id - instance = entry_point.load() - instance.name = entry_point_name - parsers.append(instance) - parsers.extend([TabularDataParser(), ArchiveParser()]) # There are some entries with PIDs that have mainfiles which do not match what @@ -284,10 +270,12 @@ if config.process.use_empty_parsers: parsers.append(BrokenParser()) -""" A dict to access parsers by name. Usually 'parsers/<...>', e.g. 'parsers/vasp'. """ + parser_dict: Dict[str, Parser] = { parser.name: parser for parser in parsers + empty_parsers -} +} # type: ignore +""" A dict to access parsers by name. Usually 'parsers/<...>', e.g. 'parsers/vasp'. """ + # renamed parsers _renames = { diff --git a/pyproject.toml b/pyproject.toml index 354fbda9a494669a75a7434bef5b5f3bcfa2213f..c21d95504eb7d1f6e50b53c0a96fa307e8e36406 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ dependencies = [ 'h5py>=3.6.0', 'hjson>=3.0.2', 'httpx>=0.23.3', - 'importlib_metadata~=7.1.0', 'jmespath>=0.10.0', 'lxml>=5.2', 'lxml-html-clean>=0.1.0', diff --git a/requirements-dev.txt b/requirements-dev.txt index 96429ce7f84175ebdf3276c49fb6bec8d509844e..b6d4a8611c4ed0ccbd38dd381b1042d04eeda9d9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -22,7 +22,7 @@ async-generator==1.10 # via -r requirements.txt, jupyterhub atpublic==4.1.0 # via aiosmtpd attrs==23.2.0 # via -r requirements.txt, aiosmtpd, jsonschema, pytest babel==2.14.0 # via -r requirements.txt, mkdocs-git-revision-date-localized-plugin, mkdocs-material, sphinx -backports-tarfile==1.1.0 # via jaraco-context +backports-tarfile==1.0.0 # via jaraco-context bagit==1.8.1 # via -r requirements.txt, nomad-lab (pyproject.toml) basicauth==0.4.1 # via -r requirements.txt, nomad-lab (pyproject.toml) beautifulsoup4==4.12.3 # via -r requirements.txt, nomad-lab (pyproject.toml) @@ -33,7 +33,7 @@ blinker==1.7.0 # via -r requirements.txt, flask blosc2==2.5.1 # via -r requirements.txt, tables build==1.2.1 # via nomad-lab (pyproject.toml), pip-tools cachetools==5.3.3 # via -r requirements.txt, nomad-lab (pyproject.toml) -celery==5.4.0 # via -r requirements.txt, nomad-lab (pyproject.toml) +celery==5.3.6 # via -r requirements.txt, nomad-lab (pyproject.toml) certifi==2024.2.2 # via -r requirements.txt, elasticsearch, httpcore, httpx, requests certipy==0.1.3 # via -r requirements.txt, jupyterhub cffi==1.16.0 # via -r requirements.txt, cryptography @@ -63,7 +63,7 @@ dnspython==2.6.1 # via -r requirements.txt, email-validator, pymongo docker==7.0.0 # via -r requirements.txt, dockerspawner dockerspawner==13.0.0 # via -r requirements.txt, nomad-lab (pyproject.toml) docstring-parser==0.16 # via -r requirements.txt, nomad-lab (pyproject.toml) -docutils==0.21.1 # via -r requirements.txt, m2r, readme-renderer, recommonmark, sphinx +docutils==0.20.1 # via -r requirements.txt, m2r, readme-renderer, recommonmark, sphinx elasticsearch==7.17.1 # via -r requirements.txt, elasticsearch-dsl, nomad-lab (pyproject.toml) elasticsearch-dsl==7.4.0 # via -r requirements.txt, nomad-lab (pyproject.toml) email-validator==1.3.1 # via -r requirements.txt, optimade @@ -107,7 +107,7 @@ idna==3.7 # via -r requirements.txt, anyio, email-validator, htt ifes-apt-tc-data-modeling==0.1 # via -r requirements.txt, pynxtools imageio==2.27.0 # via -r requirements.txt, hyperspy, kikuchipy, nionswift, nionswift-io, nionui, scikit-image imagesize==1.4.1 # via -r requirements.txt, sphinx -importlib-metadata==7.1.0 # via -r requirements.txt, build, dask, flask, hyperspy, jupyter-client, jupyterhub, keyring, markdown, mkdocs, nomad-lab (pyproject.toml), pynxtools, sphinx, twine +importlib-metadata==7.1.0 # via -r requirements.txt, build, dask, flask, hyperspy, jupyter-client, jupyterhub, keyring, markdown, mkdocs, pynxtools, sphinx, twine importlib-resources==6.4.0 # via -r requirements.txt, matplotlib, spglib inflection==0.5.1 # via -r requirements.txt, nomad-lab (pyproject.toml) ipykernel==6.29.4 # via -r requirements.txt, ipyparallel @@ -115,7 +115,7 @@ ipyparallel==8.8.0 # via -r requirements.txt, hyperspy ipython==8.18.1 # via -r requirements.txt, hyperspy, ipykernel, ipyparallel, pynxtools-stm isodate==0.6.1 # via -r requirements.txt, rdflib isoduration==20.11.0 # via -r requirements.txt, jsonschema -itsdangerous==2.2.0 # via -r requirements.txt, flask, nomad-lab (pyproject.toml) +itsdangerous==2.1.2 # via -r requirements.txt, flask, nomad-lab (pyproject.toml) jaraco-classes==3.4.0 # via keyring jaraco-context==5.3.0 # via keyring jaraco-functools==4.0.0 # via keyring @@ -165,7 +165,7 @@ mkdocs-redirects==1.2.1 # via nomad-lab (pyproject.toml) mmtf-python==1.1.3 # via -r requirements.txt, mdanalysis mongoengine==0.28.2 # via -r requirements.txt, nomad-lab (pyproject.toml) mongomock==4.1.2 # via -r requirements.txt, optimade -monty==2024.4.17 # via -r requirements.txt, pymatgen +monty==2024.3.31 # via -r requirements.txt, pymatgen more-itertools==10.2.0 # via jaraco-classes, jaraco-functools, pytest mpmath==1.3.0 # via -r requirements.txt, sympy mrcfile==1.5.0 # via -r requirements.txt, griddataformats @@ -213,7 +213,7 @@ pint==0.17 # via -r requirements.txt, hyperspy, nomad-lab (pyproj pip-tools==7.4.1 # via nomad-lab (pyproject.toml) pkginfo==1.10.0 # via twine platformdirs==4.2.0 # via -r requirements.txt, jupyter-core, mkdocs, pooch -plotly==5.21.0 # via -r requirements.txt, asr, pymatgen +plotly==5.20.0 # via -r requirements.txt, asr, pymatgen pluggy==0.13.1 # via pytest pooch==1.8.1 # via -r requirements.txt, kikuchipy, orix prettytable==3.10.0 # via -r requirements.txt, hyperspy @@ -274,7 +274,7 @@ rdflib==5.0.0 # via -r requirements.txt, nomad-lab (pyproject.toml) rdkit==2023.9.5 # via -r requirements.txt, nomad-lab (pyproject.toml) readme-renderer==43.0 # via twine recommonmark==0.7.1 # via -r requirements.txt, nomad-lab (pyproject.toml) -regex==2024.4.16 # via mkdocs-material +regex==2023.12.25 # via mkdocs-material requests==2.31.0 # via -r requirements.txt, docker, hyperspy, jupyterhub, mkdocs-material, nomad-lab (pyproject.toml), oauthenticator, optimade, pooch, pybis, pymatgen, python-gitlab, python-keycloak, requests-toolbelt, rfc3161ng, sphinx, twine requests-toolbelt==1.0.0 # via -r requirements.txt, python-gitlab, python-keycloak, twine rfc3161ng==2.1.3 # via -r requirements.txt, nomad-lab (pyproject.toml) @@ -299,7 +299,7 @@ snowballstemmer==2.2.0 # via -r requirements.txt, sphinx soupsieve==2.5 # via -r requirements.txt, beautifulsoup4 sparse==0.15.1 # via -r requirements.txt, hyperspy spglib==2.4.0 # via -r requirements.txt, asr, matid, phonopy, pymatgen -sphinx==7.3.6 # via -r requirements.txt, recommonmark +sphinx==7.2.6 # via -r requirements.txt, recommonmark sphinxcontrib-applehelp==1.0.8 # via -r requirements.txt, sphinx sphinxcontrib-devhelp==1.0.6 # via -r requirements.txt, sphinx sphinxcontrib-htmlhelp==2.0.5 # via -r requirements.txt, sphinx @@ -317,8 +317,8 @@ tenacity==8.2.3 # via -r requirements.txt, plotly termcolor==2.4.0 # via mkdocs-macros-plugin texttable==1.7.0 # via -r requirements.txt, pybis threadpoolctl==3.4.0 # via -r requirements.txt, mdanalysis, scikit-learn -tifffile==2024.4.18 # via -r requirements.txt, h5grove, hyperspy, scikit-image -tomli==2.0.1 # via -r requirements.txt, build, mypy, pip-tools, pyproject-hooks, sphinx +tifffile==2024.2.12 # via -r requirements.txt, h5grove, hyperspy, scikit-image +tomli==2.0.1 # via build, mypy, pip-tools, pyproject-hooks toolz==0.12.1 # via -r requirements.txt, dask, hyperspy, partd toposort==1.10 # via -r requirements.txt, nomad-lab (pyproject.toml) tornado==6.4 # via -r requirements.txt, ipykernel, ipyparallel, jupyter-client, jupyterhub diff --git a/requirements.txt b/requirements.txt index 189c613473a61272643a7bb6fd134d79375f42f0..f5ab4edbc8f85682b94777d8227bad6876635918 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ bitarray==2.9.2 # via nomad-lab (pyproject.toml), nomad_dos_fingerprin blinker==1.7.0 # via flask blosc2==2.5.1 # via tables cachetools==5.3.3 # via nomad-lab (pyproject.toml) -celery==5.4.0 # via nomad-lab (pyproject.toml) +celery==5.3.6 # via nomad-lab (pyproject.toml) certifi==2024.2.2 # via elasticsearch, httpcore, httpx, requests certipy==0.1.3 # via jupyterhub cffi==1.16.0 # via cryptography @@ -55,7 +55,7 @@ dnspython==2.6.1 # via email-validator, pymongo docker==7.0.0 # via dockerspawner dockerspawner==13.0.0 # via nomad-lab (pyproject.toml) docstring-parser==0.16 # via nomad-lab (pyproject.toml) -docutils==0.21.1 # via m2r, recommonmark, sphinx +docutils==0.20.1 # via m2r, recommonmark, sphinx elasticsearch==7.17.1 # via elasticsearch-dsl, nomad-lab (pyproject.toml) elasticsearch-dsl==7.4.0 # via nomad-lab (pyproject.toml) email-validator==1.3.1 # via optimade @@ -96,7 +96,7 @@ idna==3.7 # via anyio, email-validator, httpx, jsonschema, reque ifes-apt-tc-data-modeling==0.1 # via pynxtools imageio==2.27.0 # via hyperspy, kikuchipy, nionswift, nionswift-io, nionui, scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 # via dask, flask, hyperspy, jupyter-client, jupyterhub, nomad-lab (pyproject.toml), pynxtools, sphinx +importlib-metadata==7.1.0 # via dask, flask, hyperspy, jupyter-client, jupyterhub, pynxtools, sphinx importlib-resources==6.4.0 # via matplotlib, spglib inflection==0.5.1 # via nomad-lab (pyproject.toml) ipykernel==6.29.4 # via ipyparallel @@ -104,7 +104,7 @@ ipyparallel==8.8.0 # via hyperspy ipython==8.18.1 # via hyperspy, ipykernel, ipyparallel, pynxtools-stm isodate==0.6.1 # via rdflib isoduration==20.11.0 # via jsonschema -itsdangerous==2.2.0 # via flask, nomad-lab (pyproject.toml) +itsdangerous==2.1.2 # via flask, nomad-lab (pyproject.toml) jedi==0.19.1 # via ipython jinja2==3.1.3 # via flask, hyperspy, jupyterhub, sphinx jmespath==1.0.1 # via nomad-lab (pyproject.toml) @@ -140,7 +140,7 @@ mistune==3.0.2 # via m2r mmtf-python==1.1.3 # via mdanalysis mongoengine==0.28.2 # via nomad-lab (pyproject.toml) mongomock==4.1.2 # via optimade -monty==2024.4.17 # via pymatgen +monty==2024.3.31 # via pymatgen mpmath==1.3.0 # via sympy mrcfile==1.5.0 # via griddataformats msgpack==1.0.8 # via blosc2, mmtf-python, nomad-lab (pyproject.toml) @@ -179,7 +179,7 @@ phonopy==2.9.1 # via asr pillow==10.0.1 # via fabio, hyperspy, imageio, matplotlib, nionswift, rdkit, scikit-image pint==0.17 # via hyperspy, nomad-lab (pyproject.toml), rosettasciio platformdirs==4.2.0 # via jupyter-core, pooch -plotly==5.21.0 # via asr, pymatgen +plotly==5.20.0 # via asr, pymatgen pooch==1.8.1 # via hyperspy, kikuchipy, orix prettytable==3.10.0 # via hyperspy prometheus-client==0.20.0 # via jupyterhub @@ -247,7 +247,7 @@ snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 # via beautifulsoup4 sparse==0.15.1 # via hyperspy spglib==2.4.0 # via asr, matid, phonopy, pymatgen -sphinx==7.3.6 # via recommonmark +sphinx==7.2.6 # via recommonmark sphinxcontrib-applehelp==1.0.8 # via sphinx sphinxcontrib-devhelp==1.0.6 # via sphinx sphinxcontrib-htmlhelp==2.0.5 # via sphinx @@ -264,8 +264,7 @@ tabulate==0.8.9 # via nomad-lab (pyproject.toml), pybis, pymatgen tenacity==8.2.3 # via plotly texttable==1.7.0 # via pybis threadpoolctl==3.4.0 # via mdanalysis, scikit-learn -tifffile==2024.4.18 # via h5grove, hyperspy, scikit-image -tomli==2.0.1 # via sphinx +tifffile==2024.2.12 # via h5grove, hyperspy, scikit-image toolz==0.12.1 # via dask, hyperspy, partd toposort==1.10 # via nomad-lab (pyproject.toml) tornado==6.4 # via ipykernel, ipyparallel, jupyter-client, jupyterhub diff --git a/tests/normalizing/conftest.py b/tests/normalizing/conftest.py index b6ae32ecdd060cc539a3594f815f470b504c1646..8f5df250fcd8c39f4d6a45fb3a4cc0b668afef86 100644 --- a/tests/normalizing/conftest.py +++ b/tests/normalizing/conftest.py @@ -71,7 +71,7 @@ from nomad.datamodel.metainfo import ( def run_normalize(entry_archive: EntryArchive) -> EntryArchive: for normalizer_class in normalizers: normalizer = normalizer_class(entry_archive) - normalizer.normalize(logger=get_logger(__name__)) + normalizer.normalize() return entry_archive diff --git a/tests/test_config.py b/tests/test_config.py index b4482e742b47166f718a9adb8d76ae2fb17cd65d..ef167f2bd93a9391ce140ca39d8ebd0189e51f5e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -173,20 +173,18 @@ def test_config_priority(conf_yaml, conf_env, value, mockopen, monkeypatch): {'plugins': {'include': ['normalizers/simulation/dos']}}, { 'plugins': { - 'entry_points': { - 'include': ['normalizers/simulation/dos'], - 'options': { - 'normalizers/simulation/dos': { - 'name': 'yaml', - 'python_package': 'dosnormalizer', - 'description': 'This is the normalizer for DOS in NOMAD.\n', - 'plugin_documentation_url': None, - 'plugin_source_code_url': None, - 'normalizer_class_name': 'dosnormalizer.DosNormalizer', - 'plugin_type': 'normalizer', - } - }, - } + 'include': ['normalizers/simulation/dos'], + 'options': { + 'normalizers/simulation/dos': { + 'name': 'yaml', + 'python_package': 'dosnormalizer', + 'description': 'This is the normalizer for DOS in NOMAD.\n', + 'plugin_documentation_url': None, + 'plugin_source_code_url': None, + 'normalizer_class_name': 'dosnormalizer.DosNormalizer', + 'plugin_type': 'normalizer', + } + }, } }, id='dictionary: merges', @@ -194,7 +192,7 @@ def test_config_priority(conf_yaml, conf_env, value, mockopen, monkeypatch): pytest.param( {'plugins': {'include': ['a']}}, {'plugins': {'include': ['b']}}, - {'plugins': {'entry_points': {'include': ['b']}}}, + {'plugins': {'include': ['b']}}, id='list: overrides', ), pytest.param( @@ -219,9 +217,9 @@ def test_parser_plugins(): config = load_config() config.load_plugins() parsers = [ - entry_point - for entry_point in config.plugins.entry_points.options.values() - if isinstance(entry_point, Parser) + plugin + for plugin in config.plugins.options.values() + if isinstance(plugin, Parser) ] assert len(parsers) == 71 @@ -246,5 +244,5 @@ def test_plugin_polymorphism(mockopen, monkeypatch): } config = load_test_config(plugins, None, mockopen, monkeypatch) config.load_plugins() - assert isinstance(config.plugins.entry_points.options['schema'], Schema) - assert isinstance(config.plugins.entry_points.options['parser'], Parser) + assert isinstance(config.plugins.options['schema'], Schema) + assert isinstance(config.plugins.options['parser'], Parser)