Skip to content

Commit

Permalink
Manage simplest case of from-import
Browse files Browse the repository at this point in the history
  • Loading branch information
jecisc committed Sep 26, 2024
1 parent be1d3f9 commit 4509152
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 24 deletions.
39 changes: 29 additions & 10 deletions src/Famix-Python-Importer-Tests/FamixPythonProject1Test.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,33 @@ FamixPythonProject1Test >> testFunctionImportsModule [
{ #category : 'tests - global variables' }
FamixPythonProject1Test >> testGlobalVariableInModule [

| method |
| global |
self denyEmpty: self model allGlobalVariables.

method := self globalVariableNamed: 'moduleAtRootVariable'.
global := self globalVariableNamed: 'moduleAtRootVariable'.

self assert: method class equals: FamixPythonGlobalVariable.
self assert: method name equals: 'moduleAtRootVariable'.
self assert: method parentScope equals: (self moduleNamed: 'moduleAtRoot')
self assert: global class equals: FamixPythonGlobalVariable.
self assert: global name equals: 'moduleAtRootVariable'.
self assert: global parentScope equals: (self moduleNamed: 'moduleAtRoot').

self assert: global sourceAnchor isNotNil.
self assert: global sourceText equals: 'moduleAtRootVariable'
]

{ #category : 'tests - global variables' }
FamixPythonProject1Test >> testGlobalVariableInPackage [

| method |
| global |
self denyEmpty: self model allGlobalVariables.

method := self globalVariableNamed: 'rootPackageVariable'.
global := self globalVariableNamed: 'rootPackageVariable'.

self assert: method class equals: FamixPythonGlobalVariable.
self assert: method name equals: 'rootPackageVariable'.
self assert: method parentScope equals: (self packageNamed: 'root')
self assert: global class equals: FamixPythonGlobalVariable.
self assert: global name equals: 'rootPackageVariable'.
self assert: global parentScope equals: (self packageNamed: 'root').

self assert: global sourceAnchor isNotNil.
self assert: global sourceText equals: 'rootPackageVariable'
]

{ #category : 'tests - imports' }
Expand Down Expand Up @@ -143,6 +149,19 @@ FamixPythonProject1Test >> testModel [
self assert: self model rootFolder equals: FamixPythonBridge parsingExamples / 'project1'
]

{ #category : 'tests - from-import' }
FamixPythonProject1Test >> testModuleImportsGlobalVariableFromAPackage [

| rootModule global import |
self denyEmpty: self model allImports.
rootModule := self moduleNamed: 'moduleAtRoot5'.
global := self globalVariableNamed: 'rootPackageVariable'.
import := rootModule outgoingImports detect: [ :pimport | pimport target name = 'rootPackageVariable' ].

self assert: import importingEntity equals: rootModule.
self assert: import importedEntity equals: global
]

{ #category : 'tests - imports' }
FamixPythonProject1Test >> testModuleImportsModuleInPackage [

Expand Down
90 changes: 90 additions & 0 deletions src/Famix-Python-Importer/FamixPythonFromImportResolvable.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Class {
#name : 'FamixPythonFromImportResolvable',
#superclass : 'SRResolvable',
#instVars : [
'path',
'entityName',
'entity'
],
#category : 'Famix-Python-Importer-Visitors',
#package : 'Famix-Python-Importer',
#tag : 'Visitors'
}

{ #category : 'accessing' }
FamixPythonFromImportResolvable class >> path: aStringPath entityName: anEntityName [

^ self new
path: aStringPath;
entityName: anEntityName;
yourself
]

{ #category : 'hooks' }
FamixPythonFromImportResolvable >> applyReplacementStrategyWithCurrentEntity: aCurrentEntity [

self entity: (self notFoundReplacementEntity cull: self cull: aCurrentEntity)
]

{ #category : 'accessing' }
FamixPythonFromImportResolvable >> entity [
^ entity
]

{ #category : 'accessing' }
FamixPythonFromImportResolvable >> entity: anObject [

entity := anObject
]

{ #category : 'accessing' }
FamixPythonFromImportResolvable >> entityName: anObject [
entityName := anObject
]

{ #category : 'accessing' }
FamixPythonFromImportResolvable >> identifier [
^ self path
]

{ #category : 'accessing' }
FamixPythonFromImportResolvable >> path [
^ path
]

{ #category : 'accessing' }
FamixPythonFromImportResolvable >> path: anObject [
path := anObject
]

{ #category : 'resolution' }
FamixPythonFromImportResolvable >> resolveAbsolutePathFor: currentEntity [

| possibleEntities splittedPath |
possibleEntities := currentEntity mooseModel rootEntities.
splittedPath := path splitOn: $..
possibleEntities := possibleEntities select: [ :possibleEntity | possibleEntity name = splittedPath first ].
splittedPath removeFirst.
[ splittedPath isNotEmpty ] whileTrue: [
| childName |
childName := splittedPath anyOne.
splittedPath removeFirst.
possibleEntities := possibleEntities flatCollect: [ :possibleEntity |
possibleEntity children select: [ :child | (child isPackage or: [ child isModule ]) and: [ child name = childName ] ] ] ].
possibleEntities ifEmpty: [ NotFound signal ].

possibleEntities size = 1 ifFalse: [ self error: 'There should be only one possible entity to import.' ].

possibleEntities := possibleEntities anyOne allChildren select: [ :child | child isNamedEntity and: [ child name = entityName ] ].
"It is possible that we have multiple entities with the same name and in that case, python select the last one declared. So we sort by source anchor position to find this one."
^ possibleEntities detectMax: [ :possibleEntity | possibleEntity sourceAnchor startPos ]
]

{ #category : 'resolution' }
FamixPythonFromImportResolvable >> resolveInScope: aScope currentEntity: currentEntity [
"If we have a dot at first, we have a relative path. Else it's an absolute path"

entity := (path beginsWith: '.')
ifTrue: [ 1 halt ]
ifFalse: [ self resolveAbsolutePathFor: currentEntity ]
]
37 changes: 23 additions & 14 deletions src/Famix-Python-Importer/FamixPythonImporterVisitor.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,7 @@ Class {
{ #category : 'visiting' }
FamixPythonImporterVisitor >> acceptNode: aNode [

^ [ super acceptNode: aNode ]
on: Exception
do: [ :exception |
(exception isKindOf: Warning)
ifTrue: [
self errorReport addWarning: exception.
exception resume ]
ifFalse: [ self errorReport addError: exception ] ]
^ self errorReport catch: Error during: [ super acceptNode: aNode ]
]

{ #category : 'visiting' }
Expand Down Expand Up @@ -138,11 +131,22 @@ FamixPythonImporterVisitor >> createImport: anImport ofName: aName from: fromNam

self currentEntity addOutgoingImport: import.
self flag: #todo. "Check how to manage the push before the main entity and the pops."
self solver
resolve: ((FamixPythonImportResolvable path: aName)
notFoundReplacementEntity: [ :unresolvedImport :currentEntity | self ensureStubPackagesFromPath: unresolvedImport path ];
yourself)
foundAction: [ :entity :currentEntity | entity addIncomingImport: import ].
self flag: #todo. "Clean code about the kind of resolution to do."

fromName
ifNil: [
self solver
resolve: ((FamixPythonImportResolvable path: aName)
notFoundReplacementEntity: [ :unresolvedImport :currentEntity | self ensureStubPackagesFromPath: unresolvedImport path ];
yourself)
foundAction: [ :entity :currentEntity | entity addIncomingImport: import ] ]
ifNotNil: [
self solver
resolve: ((FamixPythonFromImportResolvable path: fromName entityName: aName)
notFoundReplacementEntity: [ :unresolvedImport :currentEntity | self ensureStubPackagesFromPath: unresolvedImport path ];
yourself)
foundAction: [ :entity :currentEntity | entity addIncomingImport: import ] ].

"pushResolvableAsScopeBeforeMainEntityScope: (FamixPythonImportResolvable path: aName)"

^ import
Expand Down Expand Up @@ -681,7 +685,12 @@ FamixPythonImporterVisitor >> visitAnnotatedSymbol: anAnnotatedSymbol [
{ #category : 'visiting' }
FamixPythonImporterVisitor >> visitAssignmentStatement: anAssignmentStatement [

(anAssignmentStatement lhs isKindOf: PyVariableExpressionNode) ifTrue: [ self ensureVariable: anAssignmentStatement lhs name localTo: self currentEntity ].
(anAssignmentStatement lhs isKindOf: PyVariableExpressionNode) ifTrue: [
self currentEntity dictLocalVariables at: anAssignmentStatement lhs name ifAbsent: [
| variable |
variable := self currentEntity createLocalVariable: anAssignmentStatement lhs name.
"We select the lhs node because the source anchor should not be the full assignation."
self setSourceAnchor: variable from: anAssignmentStatement lhs ] ].

^ super visitAssignmentStatement: anAssignmentStatement
]
Expand Down

0 comments on commit 4509152

Please sign in to comment.